
#include "precomp.h"
#include "Driver.h"
#include "dnsfilter.tmh"

LIST_ENTRY  g_stOpenedFileList;
LIST_ENTRY  g_stAcceptableList;
LIST_ENTRY  g_stPendedPacketsQueue;
HANDLE      g_hInjectionHandle = NULL;
HANDLE      g_hInjectionHandleV6 = NULL;
UINT32      g_nAleFlowEstablishedId = 0;
UINT32      g_nAleFlowEstablishedV6Id = 0;
UINT32      g_nStreamId = 0;
UINT32      g_nStreamIdV6 = 0;
UINT32      g_nDatagramId = 0;
UINT32      g_nDatagramIdV6 = 0;
KEVENT      g_oWorkerEvent;
KEVENT      g_oWorkerDoneEvent;
BOOLEAN     g_bDriverUnload = FALSE;
PVOID       g_pWorkerThreadObj = NULL;
const UINT32 c_ulLocalHost = HOST_ADDRESS(127,0,0,1);
NDIS_HANDLE g_hNetBufferListPool = NULL;
NDIS_HANDLE g_hNetBufferPool = NULL;
PNDIS_GENERIC_OBJECT    g_pNdisGenericObj = NULL;

void FilterDataCleanup(
    PADDRESSES_DESCRIPTOR pDeviceAddressesDescriptor
    )
{
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    if (NULL != pDeviceAddressesDescriptor->m_pDataArray)
    {
        ExFreePoolWithTag(
            pDeviceAddressesDescriptor->m_pDataArray,
            POOL_TAG
            );
    }
    RtlZeroMemory(
        pDeviceAddressesDescriptor,
        sizeof(ADDRESSES_DESCRIPTOR)
        );
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
}

NTSTATUS dnsfilterCoInitialize(PDEVICE_OBJECT deviceObject)
{
	NTSTATUS    status;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    InitializeListHead(&g_stOpenedFileList);
    InitializeListHead(&g_stAcceptableList);
	InitializeListHead(&g_stPendedPacketsQueue);
    KeInitializeEvent(
        &g_oWorkerEvent,
        NotificationEvent,
        FALSE
        );
    KeInitializeEvent(
        &g_oWorkerDoneEvent,
        NotificationEvent,
        FALSE
        );
	status = FwpsInjectionHandleCreate(
		AF_INET,
		FWPS_INJECTION_TYPE_STREAM | FWPS_INJECTION_TYPE_TRANSPORT | FWPS_INJECTION_TYPE_NETWORK,
		&g_hInjectionHandle
		);
	if(!NT_SUCCESS(status))
	{
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsInjectionHandleCreate for AF_INET failed with status %!STATUS!",
            status
            );
		return status;
	}
	status = FwpsInjectionHandleCreate(
        AF_INET6,
		FWPS_INJECTION_TYPE_STREAM | FWPS_INJECTION_TYPE_TRANSPORT | FWPS_INJECTION_TYPE_NETWORK,
		&g_hInjectionHandleV6
		);
	if(!NT_SUCCESS(status))
	{
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsInjectionHandleCreate for AF_INET6 failed with status %!STATUS!",
            status
            );
		return status;
	}
	status = dnsfilterCoRegisterCallouts(deviceObject);
	if(!NT_SUCCESS(status))
	{
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallouts failed with status %!STATUS!",
            status
            );
		return status;
	}
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
    return status;
}

NTSTATUS
dnsfilterCoRegisterCallouts(
   PDEVICE_OBJECT deviceObject
   )
{
    NTSTATUS status;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    //
    // We won't be called for flow deletion for the flow established layer 
    // since we only establish a flow for the stream layer, so we don't
    // specify a flow deletion function.
    //
    status = dnsfilterCoRegisterCallout(
        deviceObject,
        onAleFlowEstablishedClassify,
        onFiltersChangeNotify,
        (FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN)NULL, // We don't need a flow delete function at this layer.
        &DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V4,
        FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY,
        &g_nAleFlowEstablishedId
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallout for DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V4 failed with status %!STATUS!",
            status
            );
        return status;
    }
    status = dnsfilterCoRegisterCallout(
        deviceObject,
        onAleFlowEstablishedV6Classify,
        onFiltersChangeNotify,
        (FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN)NULL, // We don't need a flow delete function at this layer.
        &DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V6,
        FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY,
        &g_nAleFlowEstablishedV6Id
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallout for DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V6 failed with status %!STATUS!",
            status
            );
        return status;
    }
    status = dnsfilterCoRegisterCallout(
        deviceObject,
        onStreamClassify,
        onFiltersChangeNotify,
        onDnsFlowDeletion,
        &DSN_FILTER_STREAM_CALLOUT_V4,
        FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY | FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW,
        &g_nStreamId
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallout for DSN_FILTER_STREAM_CALLOUT_V4 failed with status %!STATUS!",
            status
            );
        return status;
    }
    status = dnsfilterCoRegisterCallout(
        deviceObject,
        onStreamClassify,
        onFiltersChangeNotify,
        onDnsFlowDeletion,
        &DSN_FILTER_STREAM_CALLOUT_V6,
        FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY | FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW,
        &g_nStreamIdV6
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallout for DSN_FILTER_STREAM_CALLOUT_V6 failed with status %!STATUS!",
            status
            );
        return status;
    }
    status = dnsfilterCoRegisterCallout(
        deviceObject,
        onDatagramDataClassify,
        onFiltersChangeNotify,
        onDnsFlowDeletion,
        &DSN_FILTER_DATAGRAM_DATA_CALLOUT_V4,
        FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY | FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW,
        &g_nDatagramId
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallout for DSN_FILTER_DATAGRAM_DATA_CALLOUT_V4 failed with status %!STATUS!",
            status
            );
        return status;
    }
    status = dnsfilterCoRegisterCallout(
        deviceObject,
        onDatagramDataClassify,
        onFiltersChangeNotify,
        onDnsFlowDeletion,
        &DSN_FILTER_DATAGRAM_DATA_CALLOUT_V6,
        FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY | FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW,
        &g_nDatagramIdV6
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! dnsfilterCoRegisterCallout for DSN_FILTER_DATAGRAM_DATA_CALLOUT_V6 failed with status %!STATUS!",
            status
            );
        return status;
    }
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
    return status;
}

NTSTATUS
dnsfilterCoRegisterCallout(
   IN OUT PDEVICE_OBJECT deviceObject,
   IN  FWPS_CALLOUT_CLASSIFY_FN ClassifyFunction,
   IN  FWPS_CALLOUT_NOTIFY_FN   NotifyFunction,
   IN  FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FlowDeleteFunction,
   IN  GUID const* calloutKey,
   IN  UINT32 flags,
   OUT UINT32* calloutId
   )
{
    FWPS_CALLOUT sCallout;
    NTSTATUS status = STATUS_SUCCESS;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    RtlZeroMemory(&sCallout, sizeof(FWPS_CALLOUT));
    sCallout.calloutKey = *calloutKey;
    sCallout.flags = flags;
    sCallout.classifyFn = ClassifyFunction;
    sCallout.notifyFn = NotifyFunction;
    sCallout.flowDeleteFn = FlowDeleteFunction;
    status = FwpsCalloutRegister(
        deviceObject,
        &sCallout,
        calloutId
        );
	if(!NT_SUCCESS(status))
	{
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsCalloutRegister0 failed with status %!STATUS!",
            status
            );
		return status;
	}
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
    return status;
}

void NTAPI onAleFlowEstablishedClassify(
    __in const FWPS_INCOMING_VALUES* inFixedValues,
    __in const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
    __inout_opt void* layerData,
    _In_opt_ const void* classifyContext,
    __in const FWPS_FILTER* filter,
    __in UINT64 flowContext,
    __out FWPS_CLASSIFY_OUT* classifyOut
    )
{
    int i;
    PADDRESSES_DESCRIPTOR   pAddressesDescriptor = NULL;
    UINT32                  ulRemoteAddress;
    BOOLEAN                 bLocalNetwork = FALSE;
    PLIST_ENTRY             pListEntry;
    PFLOW_DATA              pFlowContext = NULL;
    WDFFILEOBJECT           wdfFlowFileObject = NULL;
    NTSTATUS                status = STATUS_SUCCESS;
    IPPROTO                 enIpProto;
    UINT64                  flowHandle = 0;
    WDFREQUEST              wdfWaitFlowEstablishedRequest;
    WDFMEMORY               wdfOutputMemory = NULL;
    WDFQUEUE                wdfRequestQueue;
    UNREFERENCED_PARAMETER(layerData);
    UNREFERENCED_PARAMETER(filter);
    UNREFERENCED_PARAMETER(classifyContext);
    UNREFERENCED_PARAMETER(flowContext);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    ASSERT(FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 == inFixedValues->layerId);
    if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))"
            );
        return;
    }
    WdfObjectAcquireLock(g_wdfControlDevice);
    pAddressesDescriptor = WdfObjectGetTypedContext(
        g_wdfControlDevice,
        ADDRESSES_DESCRIPTOR
        );
    if(NULL == pAddressesDescriptor)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (NULL == pAddressesDescriptor)"
            );
        return;
    }
    if(NULL == pAddressesDescriptor->m_pDataArray)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (NULL == pAddressesDescriptor->m_pDataArray)"
            );
        return;
    }
    ulRemoteAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32;
    if(c_ulLocalHost == ulRemoteAddress)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (c_ulLocalHost == ulRemoteAddress)"
            );
        return;
    }
    if(NULL != pAddressesDescriptor->m_pPublicIpArray)
    {
        for(i=0;i<pAddressesDescriptor->m_nPublicIpCount;i++)
        {
            if(ulRemoteAddress == pAddressesDescriptor->m_pPublicIpArray[i])
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if(bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if(NULL != pAddressesDescriptor->m_pPrivateIpArray)
    {
        for(i=0;i<pAddressesDescriptor->m_nPrivateIpCount;i++)
        {
            if(ulRemoteAddress == pAddressesDescriptor->m_pPrivateIpArray[i])
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if(bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if(NULL != pAddressesDescriptor->m_pPublicRangeArray)
    {
        for(i=0;i<pAddressesDescriptor->m_nPublicRangeCount;i++)
        {
            if((ulRemoteAddress >= pAddressesDescriptor->m_pPublicRangeArray[i]._ulLowValue)
                && (ulRemoteAddress <= pAddressesDescriptor->m_pPublicRangeArray[i]._ulHighValue)
                )
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if(bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if(NULL != pAddressesDescriptor->m_pPrivateRangeArray)
    {
        for(i=0;i<pAddressesDescriptor->m_nPrivateRangeCount;i++)
        {
            if((ulRemoteAddress >= pAddressesDescriptor->m_pPrivateRangeArray[i]._ulLowValue)
                && (ulRemoteAddress <= pAddressesDescriptor->m_pPrivateRangeArray[i]._ulHighValue)
                )
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if(bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if(NULL != pAddressesDescriptor->m_pPublicSubnetArray)
    {
        for(i=0;i<pAddressesDescriptor->m_nPublicSubnetCount;i++)
        {
            UINT32  ulAddrPart = (ulRemoteAddress & pAddressesDescriptor->m_pPublicSubnetArray[i]._ulIpMask);
            if(ulAddrPart == pAddressesDescriptor->m_pPublicSubnetArray[i]._ulIpAddr)
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if(bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if(NULL != pAddressesDescriptor->m_pPrivateSubnetArray)
    {
        for(i=0;i<pAddressesDescriptor->m_nPrivateSubnetCount;i++)
        {
            UINT32  ulAddrPart = (ulRemoteAddress & pAddressesDescriptor->m_pPrivateSubnetArray[i]._ulIpMask);
            if(ulAddrPart == pAddressesDescriptor->m_pPrivateSubnetArray[i]._ulIpAddr)
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if(bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if(IsListEmpty(&g_stAcceptableList))
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (IsListEmpty(&g_stAcceptableList))"
            );
        return;
    }
    pListEntry = RemoveHeadList(&g_stAcceptableList);
    pFlowContext = CONTAINING_RECORD(pListEntry, FLOW_DATA, listEntry);
    ASSERT(pFlowContext == pFlowContext->pThis);
    wdfFlowFileObject = pFlowContext->wdfFlowFileObject;
    enIpProto = (IPPROTO)inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8;
    pFlowContext->enIpProto = enIpProto;
    pFlowContext->nRemoteAddressV4  = ulRemoteAddress;
    pFlowContext->nRemoteAddressV4N = RtlUlongByteSwap(ulRemoteAddress);
    ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                          FWPS_METADATA_FIELD_FLOW_HANDLE));
    flowHandle = inMetaValues->flowHandle;
    ASSERT(0 != flowHandle);
    pFlowContext->flowHandle = flowHandle;
    ASSERT(csWaitFlowEstablishedStarted == pFlowContext->enContextState);
    if(enIpProto == IPPROTO_TCP) // TCP
    {
        status = FwpsFlowAssociateContext(
            flowHandle,
            FWPS_LAYER_STREAM_V4,
            g_nStreamId,
            (UINT64)pFlowContext
            );
        pFlowContext->enContextState = csTcpFlowEstablished;
        push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
        TraceEvents(
            TRACE_LEVEL_VERBOSE,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsFlowAssociateContext -- FWPS_LAYER_STREAM_V4"
            );
    }
    else // UDP
    {
        status = FwpsFlowAssociateContext(
            flowHandle,
            FWPS_LAYER_DATAGRAM_DATA_V4,
            g_nDatagramId,
            (UINT64)pFlowContext
            );
        pFlowContext->enContextState = csUdpFlowEstablished;
        push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
        TraceEvents(
            TRACE_LEVEL_VERBOSE,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsFlowAssociateContext -- FWPS_LAYER_DATAGRAM_DATA_V4"
            );
    }
    ASSERT(NT_SUCCESS(status));
    wdfRequestQueue = pFlowContext->wdfWaitFlowEstablishedQueue;
    ASSERT(NULL != wdfRequestQueue);
    pFlowContext->wdfWaitFlowEstablishedQueue = NULL;
    status = WdfIoQueueRetrieveNextRequest(
        wdfRequestQueue,
        &wdfWaitFlowEstablishedRequest
        );
    ASSERT(NT_SUCCESS(status));
    status = WdfRequestRetrieveOutputMemory(
        wdfWaitFlowEstablishedRequest,
        &wdfOutputMemory
        );
    ASSERT(NT_SUCCESS(status));
    status = WdfMemoryCopyFromBuffer(
        wdfOutputMemory,
        0,
        pFlowContext,
        sizeof(FLOW_DATA)
        );
    ASSERT(NT_SUCCESS(status));
    WdfRequestCompleteWithInformation(
        wdfWaitFlowEstablishedRequest,
        status,
        sizeof(FLOW_DATA)
        );
    WdfObjectDelete(wdfRequestQueue);
    WdfObjectReleaseLock(g_wdfControlDevice);
    classifyOut->actionType = FWP_ACTION_PERMIT;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
}

void NTAPI onAleFlowEstablishedV6Classify(
    __in const FWPS_INCOMING_VALUES* inFixedValues,
    __in const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
    __inout_opt void* layerData,
    _In_opt_ const void* classifyContext,
    __in const FWPS_FILTER* filter,
    __in UINT64 flowContext,
    __out FWPS_CLASSIFY_OUT* classifyOut
    )
{
    int i;
    PADDRESSES_DESCRIPTOR   pAddressesDescriptor = NULL;
    FWP_BYTE_ARRAY16        *pRemoteAddress;
    BOOLEAN                 bLocalNetwork = FALSE;
    PLIST_ENTRY             pListEntry;
    PFLOW_DATA              pFlowContext = NULL;
    WDFFILEOBJECT           wdfFlowFileObject = NULL;
    NTSTATUS                status = STATUS_SUCCESS;
    IPPROTO                 enIpProto;
    UINT64                  flowHandle = 0;
    WDFREQUEST              wdfWaitFlowEstablishedRequest;
    WDFMEMORY               wdfOutputMemory = NULL;
    WDFQUEUE                wdfRequestQueue;
    UNREFERENCED_PARAMETER(layerData);
    UNREFERENCED_PARAMETER(filter);
    UNREFERENCED_PARAMETER(classifyContext);
    UNREFERENCED_PARAMETER(flowContext);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    ASSERT(FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 == inFixedValues->layerId);
    if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))"
            );
        return;
    }
    pRemoteAddress = inFixedValues->incomingValue->value.byteArray16;
    WdfObjectAcquireLock(g_wdfControlDevice);
    pAddressesDescriptor = WdfObjectGetTypedContext(
        g_wdfControlDevice,
        ADDRESSES_DESCRIPTOR
        );
    if (NULL == pAddressesDescriptor)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (NULL == pAddressesDescriptor)"
            );
        return;
    }
    if (NULL == pAddressesDescriptor->m_pDataArray)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (NULL == pAddressesDescriptor->m_pDataArray)"
            );
        return;
    }
    if (!bfIPv6AddressInIPv6Subnet(pRemoteAddress, c_pGlobalIPv6Subnet))
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (!bfIPv6AddressInIPv6Subnet(pRemoteAddress, &c_stGlobalIPv6Subnet))"
            );
        return;
    }
    if (NULL != pAddressesDescriptor->m_pPublicIPV6AddrArray)
    {
        for (i = 0; i<pAddressesDescriptor->m_nPublicIPV6AddrCount; i++)
        {
            if (bfIPv6AddressInIPv6Subnet(pRemoteAddress, &pAddressesDescriptor->m_pPublicIPV6AddrArray[i]))
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if (bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if (NULL != pAddressesDescriptor->m_pPrivateIPV6AddrArray)
    {
        for (i = 0; i<pAddressesDescriptor->m_nPrivateIPV6AddrCount; i++)
        {
            if (bfIPv6AddressInIPv6Subnet(pRemoteAddress, &pAddressesDescriptor->m_pPrivateIPV6AddrArray[i]))
            {
                bLocalNetwork = TRUE;
                break;
            }
        }
    }
    if (bLocalNetwork)
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (bLocalNetwork)"
            );
        return;
    }
    if (IsListEmpty(&g_stAcceptableList))
    {
        WdfObjectReleaseLock(g_wdfControlDevice);
        classifyOut->actionType = FWP_ACTION_PERMIT;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (IsListEmpty(&g_stAcceptableList))"
            );
        return;
    }
    pListEntry = RemoveHeadList(&g_stAcceptableList);
    pFlowContext = CONTAINING_RECORD(pListEntry, FLOW_DATA, listEntry);
    ASSERT(pFlowContext == pFlowContext->pThis);
    wdfFlowFileObject = pFlowContext->wdfFlowFileObject;
    pFlowContext->bIPv6 = TRUE;
    enIpProto = (IPPROTO) inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8;
    pFlowContext->enIpProto = enIpProto;
    IPv6CopyAddress(&pFlowContext->stRemoteAddressV6, pRemoteAddress);
    ASSERT(
        FWPS_IS_METADATA_FIELD_PRESENT(
            inMetaValues,
            FWPS_METADATA_FIELD_FLOW_HANDLE
            )
        );
    flowHandle = inMetaValues->flowHandle;
    ASSERT(0 != flowHandle);
    pFlowContext->flowHandle = flowHandle;
    ASSERT(csWaitFlowEstablishedStarted == pFlowContext->enContextState);
    if (enIpProto == IPPROTO_TCP) // TCP
    {
        status = FwpsFlowAssociateContext(
            flowHandle,
            FWPS_LAYER_STREAM_V6,
            g_nStreamIdV6,
            (UINT64) pFlowContext
            );
        pFlowContext->enContextState = csTcpFlowEstablished;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        TraceEvents(
            TRACE_LEVEL_VERBOSE,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsFlowAssociateContext -- FWPS_LAYER_STREAM_V6"
            );
    }
    else // UDP
    {
        status = FwpsFlowAssociateContext(
            flowHandle,
            FWPS_LAYER_DATAGRAM_DATA_V6,
            g_nDatagramIdV6,
            (UINT64) pFlowContext
            );
        pFlowContext->enContextState = csUdpFlowEstablished;
        push_css(pFlowContext->arContextStateStack, pFlowContext->enContextState);
        TraceEvents(
            TRACE_LEVEL_VERBOSE,
            TRACE_DNSFILTER,
            "%!FUNC! FwpsFlowAssociateContext -- FWPS_LAYER_DATAGRAM_DATA_V6"
            );
    }
    ASSERT(NT_SUCCESS(status));
    wdfRequestQueue = pFlowContext->wdfWaitFlowEstablishedQueue;
    ASSERT(NULL != wdfRequestQueue);
    pFlowContext->wdfWaitFlowEstablishedQueue = NULL;
    status = WdfIoQueueRetrieveNextRequest(
        wdfRequestQueue,
        &wdfWaitFlowEstablishedRequest
        );
    ASSERT(NT_SUCCESS(status));
    status = WdfRequestRetrieveOutputMemory(
        wdfWaitFlowEstablishedRequest,
        &wdfOutputMemory
        );
    ASSERT(NT_SUCCESS(status));
    status = WdfMemoryCopyFromBuffer(
        wdfOutputMemory,
        0,
        pFlowContext,
        sizeof(FLOW_DATA)
        );
    ASSERT(NT_SUCCESS(status));
    WdfRequestCompleteWithInformation(
        wdfWaitFlowEstablishedRequest,
        status,
        sizeof(FLOW_DATA)
        );
    WdfObjectDelete(wdfRequestQueue);
    WdfObjectReleaseLock(g_wdfControlDevice);
    classifyOut->actionType = FWP_ACTION_PERMIT;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
}

NTSTATUS NTAPI onFiltersChangeNotify(
    __in FWPS_CALLOUT_NOTIFY_TYPE notifyType,
    __in const GUID* filterKey,
    __inout FWPS_FILTER* filter
    )
{
    UNREFERENCED_PARAMETER(notifyType);
    UNREFERENCED_PARAMETER(filterKey);
    UNREFERENCED_PARAMETER(filter);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
    return STATUS_SUCCESS;
}

void NTAPI onDatagramDataClassify(
    __in const FWPS_INCOMING_VALUES* inFixedValues,
    __in const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
    __inout_opt void* layerData,
    _In_opt_ const void* classifyContext,
    __in const FWPS_FILTER* filter,
    __in UINT64 flowContext,
    __out FWPS_CLASSIFY_OUT* classifyOut
    )
{
    FWP_DIRECTION               enPacketDirection;
    WDFFILEOBJECT               wdfFlowFileObject = NULL;
    WDFFILEOBJECT               wdfInjectDataObject = NULL;
    NTSTATUS                    status = STATUS_SUCCESS, status1;
    BOOLEAN                     bInbound;
    BOOLEAN                     bIPv6;
    PNET_BUFFER_LIST            pNetBufferList;
    FWPS_PACKET_INJECTION_STATE packetState;
    PFLOW_DATA                  pFlowData;
    PNET_BUFFER                 pNetBuffer;
    UINT32                      nDataSize;
    UINT32                      ulUdpDataOffset;
    UINT32                      ulUdpDataLength;
    UINT32                      ulIpHeaderSize;
    UINT32                      ulTransportHeaderSize;
    WDFREQUEST                  wdfPendedRequest;
    WDFMEMORY                   wdfOutputMemory = NULL;
    COMPARTMENT_ID              enUdpCompartmentID;
    WDFDEVICE                   wdfDeviceObject;
    WDFQUEUE                    wdfRequestQueue;
    UINT64                      hFlowHandle;
    HANDLE                      hInjectionHandle;
    PMDL                        _pMdl;
    WSACMSGHDR*                 _pControlData;
    UNREFERENCED_PARAMETER(classifyContext);
    UNREFERENCED_PARAMETER(filter);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    //
    // We don't have the necessary right to alter the classify, exit.
    //
    if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0)
    {
        //
        // Cannot alter the action.
        //
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))"
            );
        return;
    }
    pNetBufferList = (PNET_BUFFER_LIST)layerData;
    ASSERT(pNetBufferList != NULL);
    pFlowData = (PFLOW_DATA) flowContext;
    ASSERT(NULL != pFlowData);
    ASSERT(pFlowData == pFlowData->pThis);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    bIPv6 = pFlowData->bIPv6;
    TraceEvents(
        TRACE_LEVEL_VERBOSE,
        TRACE_DNSFILTER,
        "%!FUNC! Datagram is IPv6: %!bool!",
        bIPv6
        );
    if (bIPv6)
    {
        hInjectionHandle = g_hInjectionHandleV6;
        enPacketDirection = (FWP_DIRECTION) inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].value.uint8;
    }
    else
    {
        hInjectionHandle = g_hInjectionHandle;
        enPacketDirection = (FWP_DIRECTION) inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint8;
    }
    packetState = FwpsQueryPacketInjectionState(
        hInjectionHandle,
        pNetBufferList,
        (HANDLE*)&wdfInjectDataObject
        );
    if((packetState == FWPS_PACKET_INJECTED_BY_SELF) || (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF))
    {
        classifyOut->actionType = FWP_ACTION_PERMIT;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSFILTER,
            "%!FUNC! Exit ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF))"
            );
        return;
    }
    bInbound = (BOOLEAN)(FWP_DIRECTION_INBOUND == enPacketDirection);
    TraceEvents(
        TRACE_LEVEL_VERBOSE,
        TRACE_DNSFILTER,
        "%!FUNC! Datagram is INBOUND: %!bool!",
        bInbound
        );
    WdfObjectAcquireLock(wdfDeviceObject);
    ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                          FWPS_METADATA_FIELD_FLOW_HANDLE));
    hFlowHandle = inMetaValues->flowHandle;
    if (hFlowHandle != pFlowData->flowHandle)
    {
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        WdfObjectReleaseLock(wdfDeviceObject);
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSFILTER,
            "%!FUNC! Exit (hFlowHandle != pFlowData->flowHandle)"
            );
        return;
    }
    pFlowData->enPacketDirection = enPacketDirection;
    if (bIPv6)
    {
        pFlowData->interfaceIndex = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_INTERFACE_INDEX].value.uint32;
        pFlowData->subInterfaceIndex = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_SUB_INTERFACE_INDEX].value.uint32;
    }
    else
    {
        pFlowData->interfaceIndex = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32;
        pFlowData->subInterfaceIndex = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32;
    }
    pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList);
    nDataSize = NET_BUFFER_DATA_LENGTH(pNetBuffer);
    ulUdpDataOffset = NET_BUFFER_DATA_OFFSET(pNetBuffer);
    enUdpCompartmentID = (COMPARTMENT_ID)inMetaValues->compartmentId;
    if(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                      FWPS_METADATA_FIELD_IP_HEADER_SIZE))
    {
        ulIpHeaderSize = inMetaValues->ipHeaderSize;
    }
    else
    {
        ulIpHeaderSize = pFlowData->ulUdpQueryIpHeaderSize;
    }
    ulTransportHeaderSize = inMetaValues->transportHeaderSize;
    TraceEvents(
        TRACE_LEVEL_VERBOSE,
        TRACE_DNSFILTER,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    switch (pFlowData->enContextState)
    {
    case csUdpFlowEstablished:
        if (!bInbound)
        {
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! if (!bInbound)"
                );
            classifyOut->actionType = FWP_ACTION_BLOCK;
            classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
            status = FwpsFlowRemoveContext(
                pFlowData->flowHandle,
                FWPS_LAYER_DATAGRAM_DATA_V4,
                g_nDatagramId
                );
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                status
                );
            if ((STATUS_SUCCESS == status) || (STATUS_PENDING == status))
            {
                break;
            }
            pFlowData->enContextState = csDnsError;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            status1 = status;
            wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
            ASSERT(NULL != wdfRequestQueue);
            pFlowData->wdfWaitDnsQueryQueue = NULL;
            status = WdfIoQueueRetrieveNextRequest(
                wdfRequestQueue,
                &wdfPendedRequest
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfRequestRetrieveOutputMemory(
                wdfPendedRequest,
                &wdfOutputMemory
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfMemoryCopyFromBuffer(
                wdfOutputMemory,
                0,
                pFlowData,
                sizeof(FLOW_DATA)
                );
            ASSERT(NT_SUCCESS(status));
            WdfRequestCompleteWithInformation(
                wdfPendedRequest,
                status1,
                sizeof(FLOW_DATA)
                );
            WdfObjectDelete(wdfRequestQueue);
            break;
        }
        pFlowData->pUdpQueryBufferList = pNetBufferList;
        pFlowData->ulUdpQueryBufferSize = nDataSize + ulIpHeaderSize + ulTransportHeaderSize;
        pFlowData->ulUdpQueryDataOffset = ulUdpDataOffset;
        pFlowData->ulUdpQueryPacketOffset = ulUdpDataOffset - ulIpHeaderSize - ulTransportHeaderSize;
        ulUdpDataLength = nDataSize;
        pFlowData->ulUdpQueryDataLength = ulUdpDataLength;
        ASSERT(pFlowData->ulUdpQueryDataLength > 0);
        pFlowData->enUdpQueryCompartmentID = enUdpCompartmentID;
        pFlowData->ulUdpQueryIpHeaderSize = ulIpHeaderSize;
        pFlowData->ulUdpQueryTransportHeaderSize = ulTransportHeaderSize;
        FwpsReferenceNetBufferList(
            pNetBufferList,
            TRUE
            );
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
        wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
        ASSERT(NULL != wdfRequestQueue);
        pFlowData->wdfWaitDnsQueryQueue = NULL;
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfRequestRetrieveOutputMemory(
            wdfPendedRequest,
            &wdfOutputMemory
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfMemoryCopyFromBuffer(
            wdfOutputMemory,
            0,
            pFlowData,
            sizeof(FLOW_DATA)
            );
        ASSERT(NT_SUCCESS(status));
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            sizeof(FLOW_DATA)
            );
        pFlowData->enContextState = csUdpWaitReadQuery;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        WdfObjectDelete(wdfRequestQueue);
        break;
    case csUdpPermitQueryDone:
    case csUdpReplaceQueryDone:
    case csUdpWaitPermitQuery:
    case csUdpWaitReplaceQueryDone:
        if(bInbound)
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Repeated UDP-Query (flow state = %u)",
                pFlowData->enContextState
                );
            classifyOut->actionType = FWP_ACTION_BLOCK;
            classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
            break;
        }
        if(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                          FWPS_METADATA_FIELD_REMOTE_SCOPE_ID))
        {
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! SCOPE_ID is present"
                );
            RtlCopyMemory(
                &pFlowData->stUdpResponceSendParams.remoteScopeId,
                &inMetaValues->remoteScopeId,
                sizeof(SCOPE_ID)
                );
        }
        else
        {
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! SCOPE_ID is not present"
                );
            RtlZeroMemory(
                &pFlowData->stUdpResponceSendParams.remoteScopeId,
                sizeof(SCOPE_ID)
                );
        }
        if (FWPS_IS_METADATA_FIELD_PRESENT(
            inMetaValues, 
            FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))
        {
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! TRANSPORT_CONTROL_DATA is present"
                );
            ASSERT(inMetaValues->controlDataLength > 0);
#pragma warning(suppress:28197)
            pFlowData->stUdpResponceSendParams.controlData = (WSACMSGHDR*)ExAllocatePoolWithTag(
                NonPagedPool,
                inMetaValues->controlDataLength,
                POOL_TAG
                );
            ASSERT(NULL != pFlowData->stUdpResponceSendParams.controlData);
            RtlCopyMemory(
                pFlowData->stUdpResponceSendParams.controlData,
                inMetaValues->controlData,
                inMetaValues->controlDataLength
                );
            pFlowData->stUdpResponceSendParams.controlDataLength = inMetaValues->controlDataLength;
        }
        else
        {
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! TRANSPORT_CONTROL_DATA is not present"
                );
            if (NULL != pFlowData->stUdpResponceSendParams.controlData)
            {
                ExFreePoolWithTag(
                    pFlowData->stUdpResponceSendParams.controlData,
                    POOL_TAG
                    );
            }
            pFlowData->stUdpResponceSendParams.controlData = NULL;
            pFlowData->stUdpResponceSendParams.controlDataLength = 0;
        }
        if (bIPv6)
        {
            pFlowData->stUdpResponceSendParams.remoteAddress = (UCHAR*) &pFlowData->stRemoteAddressV6;
        }
        else
        {
            pFlowData->stUdpResponceSendParams.remoteAddress = (UCHAR*) &pFlowData->nRemoteAddressV4N;
        }
        wdfRequestQueue = pFlowData->wdfWaitUdpResponceQueue;
        ASSERT(NULL != wdfRequestQueue);
        pFlowData->wdfWaitUdpResponceQueue = NULL;
        FwpsReferenceNetBufferList(
            pNetBufferList,
            TRUE
            );
        pFlowData->pUdpResponceBufferList = pNetBufferList;
        pFlowData->ulUdpResponceBufferSize = nDataSize;
        ulUdpDataOffset = ulTransportHeaderSize;
        pFlowData->ulUdpResponceDataOffset = ulUdpDataOffset;
        ulUdpDataLength = nDataSize - ulTransportHeaderSize;
        pFlowData->nUdpResponceDataLength = ulUdpDataLength;
        pFlowData->enUdpResponceCompartmentID = enUdpCompartmentID;
        pFlowData->ulUdpResponceIpHeaderSize = ulIpHeaderSize;
        pFlowData->ulUdpResponceTransportHeaderSize = ulTransportHeaderSize;
        ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                              FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE));
        pFlowData->hUdpResponceEndpointHandle = inMetaValues->transportEndpointHandle;
        pFlowData->hUdpInjectEndpointHandle = inMetaValues->transportEndpointHandle;
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
        switch(pFlowData->enContextState)
        {
        case csUdpPermitQueryDone:
        case csUdpReplaceQueryDone:
            pFlowData->enContextState = csUdpWaitReadResponce;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        case csUdpWaitPermitQuery:
        case csUdpWaitReplaceQueryDone:
            pFlowData->enContextState = csUdpWaitQueryInjectDone;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        }
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfRequestRetrieveOutputMemory(
            wdfPendedRequest,
            &wdfOutputMemory
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfMemoryCopyFromBuffer(
            wdfOutputMemory,
            0,
            pFlowData,
            sizeof(FLOW_DATA)
            );
        ASSERT(NT_SUCCESS(status));
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            sizeof(FLOW_DATA)
            );
        WdfObjectDelete(wdfRequestQueue);
        break;
    case csUdpWaitReadQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitReadQuery\""
            );
        goto _cont03;
    case csUdpWaitApprovalQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitApprovalQuery\""
            );
        goto _cont03;
    case csUdpWaitReplaceQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitReplaceQuery\""
            );
        goto _cont03;
    case csUdpWaitResponceStarted:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitResponceStarted\""
            );
        goto _cont03;
    case csUdpPermitQueryPended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpPermitQueryPended\""
            );
        goto _cont03;
    case csUdpReplaceQueryPended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpReplaceQueryPended\""
            );
        goto _cont03;
    case csUdpQueryDiscarded:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpQueryDiscarded\""
            );
        goto _cont03;
    case csUdpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitQueryInjectDone\""
            );
        goto _cont03;
    case csUdpWaitReplaceResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitReplaceResponce\""
            );
        goto _cont03;
    case csUdpWaitApprovalResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitApprovalResponce\""
            );
        goto _cont03;
    case csUdpWaitReadResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitReadResponce\""
            );
        goto _cont03;
    case csUdpPermitResponcePended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpPermitResponcePended\""
            );
        goto _cont03;
    case csUdpWaitPermitResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitPermitResponce\""
            );
        goto _cont03;
    case csUdpReplaceResponcePended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpReplaceResponcePended\""
            );
        goto _cont03;
    case csUdpWaitReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid UDP-flow state = \"csUdpWaitReplaceResponceDone\""
            );
        goto _cont03;
    _cont03:
        if (bInbound)
        {
            TraceEvents(
                TRACE_LEVEL_WARNING,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Repeated UDP-Query"
                );
            classifyOut->actionType = FWP_ACTION_BLOCK;
            classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
            break;
        }
        switch (pFlowData->enContextState)
        {
        case csUdpWaitReadQuery:
        case csUdpWaitApprovalQuery:
            pNetBufferList = pFlowData->pUdpQueryBufferList;
            pFlowData->pUdpQueryBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsDereferenceNetBufferList(
                    pNetBufferList,
                    FALSE
                    );
            }
            break;
        case csUdpWaitReplaceQuery:
        case csUdpReplaceQueryPended:
            pNetBufferList = pFlowData->pUdpQueryReplaceBufferList;
            pFlowData->pUdpQueryReplaceBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsFreeNetBufferList(
                    pNetBufferList
                    );
            }
            _pMdl = pFlowData->pUdpQueryMdl;
            pFlowData->pUdpQueryMdl = NULL;
            if (NULL != _pMdl)
            {
                IoFreeMdl(_pMdl);
                ExFreePoolWithTag(
                    _pMdl->MappedSystemVa,
                    POOL_TAG
                    );
            }
            break;
        case csUdpWaitResponceStarted:
            pNetBufferList = pFlowData->pUdpQueryBufferList;
            pFlowData->pUdpQueryBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsDereferenceNetBufferList(
                    pNetBufferList,
                    FALSE
                    );
            }
            break;
        case csUdpPermitQueryPended:
            pNetBufferList = pFlowData->pUdpQueryCloneBufferList;
            pFlowData->pUdpQueryCloneBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsFreeCloneNetBufferList(
                    pNetBufferList,
                    0
                    );
            }
            break;
        case csUdpWaitQueryInjectDone:
            switch (pFlowData->arContextStateStack[1])
            {
            case csUdpWaitReplaceQueryDone:
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitQueryInjectDone\""
                    );
                pNetBufferList = pFlowData->pUdpQueryReplaceBufferList;
                pFlowData->pUdpQueryReplaceBufferList = NULL;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeNetBufferList(
                        pNetBufferList
                        );
                }
                _pMdl = pFlowData->pUdpQueryMdl;
                pFlowData->pUdpQueryMdl = NULL;
                if (NULL != _pMdl)
                {
                    IoFreeMdl(_pMdl);
                    ExFreePoolWithTag(
                        _pMdl->MappedSystemVa,
                        POOL_TAG
                        );
                }
                break;
            case csUdpWaitPermitQuery:
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitPermitQuery\""
                    );
                pNetBufferList = pFlowData->pUdpQueryCloneBufferList;
                pFlowData->pUdpQueryCloneBufferList = NULL;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeCloneNetBufferList(
                        pNetBufferList,
                        0
                        );
                }
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                    );
                break;
            }
            break;
        case csUdpWaitReplaceResponce:
        case csUdpWaitApprovalResponce:
            pNetBufferList = pFlowData->pUdpResponceBufferList;
            pFlowData->pUdpResponceBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsDereferenceNetBufferList(
                    pNetBufferList,
                    FALSE
                    );
            }
            pNetBufferList = pFlowData->pUdpResponceCloneBufferList;
            pFlowData->pUdpResponceCloneBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsFreeCloneNetBufferList(
                    pNetBufferList,
                    0
                    );
            }
            _pControlData = pFlowData->stUdpResponceSendParams.controlData;
            if (NULL != _pControlData)
            {
                ExFreePoolWithTag(
                    _pControlData,
                    POOL_TAG
                    );
            }
            RtlZeroMemory(
                &pFlowData->stUdpResponceSendParams,
                sizeof(FWPS_TRANSPORT_SEND_PARAMS)
                );
            break;
        case csUdpWaitReadResponce:
            switch (pFlowData->arContextStateStack[1])
            {
            case csUdpWaitReplaceQueryDone:
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitQueryInjectDone\""
                    );
                pNetBufferList = pFlowData->pUdpQueryReplaceBufferList;
                pFlowData->pUdpQueryReplaceBufferList = NULL;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeNetBufferList(
                        pNetBufferList
                        );
                }
                _pMdl = pFlowData->pUdpQueryMdl;
                pFlowData->pUdpQueryMdl = NULL;
                if (NULL != _pMdl)
                {
                    IoFreeMdl(_pMdl);
                    ExFreePoolWithTag(
                        _pMdl->MappedSystemVa,
                        POOL_TAG
                        );
                }
                break;
            case csUdpWaitPermitQuery:
                TraceEvents(
                    TRACE_LEVEL_WARNING,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitPermitQuery\""
                    );
                pNetBufferList = pFlowData->pUdpQueryCloneBufferList;
                pFlowData->pUdpQueryCloneBufferList = NULL;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeCloneNetBufferList(
                        pNetBufferList,
                        0
                        );
                }
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                    );
                break;
            }
            break;
        case csUdpPermitResponcePended:
        case csUdpWaitPermitResponce:
            pNetBufferList = pFlowData->pUdpResponceCloneBufferList;
            pFlowData->pUdpResponceCloneBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsFreeCloneNetBufferList(
                    pNetBufferList,
                    0
                    );
            }
            _pControlData = pFlowData->stUdpResponceSendParams.controlData;
            if (NULL != _pControlData)
            {
                ExFreePoolWithTag(
                    _pControlData,
                    POOL_TAG
                    );
            }
            RtlZeroMemory(
                &pFlowData->stUdpResponceSendParams,
                sizeof(FWPS_TRANSPORT_SEND_PARAMS)
                );
            break;
        case csUdpReplaceResponcePended:
        case csUdpWaitReplaceResponceDone:
            pNetBufferList = pFlowData->pUdpResponceReplaceBufferList;
            pFlowData->pUdpResponceReplaceBufferList = NULL;
            if (NULL != pNetBufferList)
            {
                FwpsFreeNetBufferList(
                    pNetBufferList
                    );
                _pControlData = pFlowData->stUdpResponceSendParams.controlData;
                if (NULL != _pControlData)
                {
                    ExFreePoolWithTag(
                        _pControlData,
                        POOL_TAG
                        );
                }
                RtlZeroMemory(
                    &pFlowData->stUdpResponceSendParams,
                    sizeof(FWPS_TRANSPORT_SEND_PARAMS)
                    );
            }
            _pMdl = pFlowData->pUdpResponceMdl;
            pFlowData->pUdpResponceMdl = NULL;
            if (NULL != _pMdl)
            {
                IoFreeMdl(_pMdl);
                ExFreePoolWithTag(
                    _pMdl->MappedSystemVa,
                    POOL_TAG
                    );
            }
            break;
        }
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        WdfObjectReleaseLock(wdfDeviceObject);
        if (bIPv6)
        {
            status = FwpsFlowRemoveContext(
                pFlowData->flowHandle,
                FWPS_LAYER_DATAGRAM_DATA_V6,
                g_nDatagramIdV6
                );
        }
        else
        {
            status = FwpsFlowRemoveContext(
                pFlowData->flowHandle,
                FWPS_LAYER_DATAGRAM_DATA_V4,
                g_nDatagramId
                );
        }
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
            status
            );
        if ((STATUS_SUCCESS == status) || (STATUS_PENDING == status))
        {
            return;
        }
        pFlowData->enContextState = csDnsError;
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        status1 = status;
        wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
        pFlowData->wdfWaitFlowDoneQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            status = WdfIoQueueRetrieveNextRequest(
                wdfRequestQueue,
                &wdfPendedRequest
                );
            if (NT_SUCCESS(status))
            {
                status = WdfRequestRetrieveOutputMemory(
                    wdfPendedRequest,
                    &wdfOutputMemory
                    );
                ASSERT(NT_SUCCESS(status));
                status = WdfMemoryCopyFromBuffer(
                    wdfOutputMemory,
                    0,
                    pFlowData,
                    sizeof(FLOW_DATA)
                    );
                ASSERT(NT_SUCCESS(status));
                WdfRequestCompleteWithInformation(
                    wdfPendedRequest,
                    status1,
                    sizeof(FLOW_DATA)
                    );
            }
            WdfObjectDelete(wdfRequestQueue);
        }
        return;
	case csUdpFlowDone:
        TraceEvents(
            TRACE_LEVEL_WARNING,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! UDP flow is Done"
            );
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
		break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
}

void NTAPI clonedDatagramDnsQueryCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject = NULL;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    WDFDEVICE       wdfDeviceObject;
    WDFQUEUE        wdfRequestQueue;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)");
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    ASSERT(pFlowData->pUdpQueryCloneBufferList == netBufferList);
    FwpsFreeCloneNetBufferList(
        netBufferList,
        0
        );
    pFlowData->pUdpQueryCloneBufferList = NULL;
    wdfRequestQueue = pFlowData->wdfUdpQueryPermitQueue;
    ASSERT(NULL != wdfRequestQueue);
    pFlowData->wdfUdpQueryPermitQueue = NULL;
    status = WdfIoQueueRetrieveNextRequest(
        wdfRequestQueue,
        &wdfPendedRequest
        );
    ASSERT(NT_SUCCESS(status));
    WdfRequestCompleteWithInformation(
        wdfPendedRequest,
        status,
        sizeof(UDP_QUERY_APPROVAL)
        );
    switch(pFlowData->enContextState)
    {
    case csUdpWaitPermitQuery:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitPermitQuery\""
            );
        pFlowData->enContextState = csUdpPermitQueryDone;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csUdpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitQueryInjectDone\""
            );
        pFlowData->enContextState = csUdpWaitReadResponce;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectDelete(wdfRequestQueue);
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI allocatedDatagramDnsQueryCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    PMDL            _pMdl;
    ULONG_PTR       Information;
    WDFDEVICE           wdfDeviceObject;
    WDFQUEUE            wdfRequestQueue;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)");
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    ASSERT(pFlowData->pUdpQueryReplaceBufferList == netBufferList);
    FwpsFreeNetBufferList(
        netBufferList
        );
    pFlowData->pUdpQueryReplaceBufferList = NULL;
    _pMdl = pFlowData->pUdpQueryMdl;
    ASSERT(NULL != _pMdl);
    IoFreeMdl(_pMdl);
    ExFreePoolWithTag(
        _pMdl->MappedSystemVa,
        POOL_TAG
        );
    pFlowData->pUdpQueryMdl = NULL;
    wdfRequestQueue = pFlowData->wdfUdpQueryReplaceQueue;
    ASSERT(NULL != wdfRequestQueue);
    pFlowData->wdfUdpQueryReplaceQueue = NULL;
    status = WdfIoQueueRetrieveNextRequest(
        wdfRequestQueue,
        &wdfPendedRequest
        );
    ASSERT(NT_SUCCESS(status));
    Information = pFlowData->ulUdpQueryReplaceDataSize;
    pFlowData->ulUdpQueryReplaceDataSize = 0;
    ASSERT(0 != Information);
    WdfRequestCompleteWithInformation(
        wdfPendedRequest,
        status,
        Information
        );
    switch(pFlowData->enContextState)
    {
    case csUdpWaitReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReplaceQueryDone\""
            );
        pFlowData->enContextState = csUdpReplaceQueryDone;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csUdpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitQueryInjectDone\""
            );
        pFlowData->enContextState = csUdpWaitReadResponce;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectDelete(wdfRequestQueue);
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
  }

void NTAPI clonedDatagramDnsResponceCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject = NULL;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    WSACMSGHDR*     _pControlData = NULL;
    WDFDEVICE           wdfDeviceObject;
    WDFQUEUE            wdfRequestQueue;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)");
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    ASSERT(csUdpWaitPermitResponce == pFlowData->enContextState);
    ASSERT(pFlowData->pUdpResponceCloneBufferList == netBufferList);
    FwpsFreeCloneNetBufferList(
        netBufferList,
        0
        );
    pFlowData->pUdpResponceCloneBufferList = NULL;
    _pControlData = pFlowData->stUdpResponceSendParams.controlData;
    if(NULL != _pControlData)
    {
        ExFreePoolWithTag(
            _pControlData,
            POOL_TAG
            );
    }
    RtlZeroMemory(
        &pFlowData->stUdpResponceSendParams,
        sizeof(FWPS_TRANSPORT_SEND_PARAMS)
        );
    wdfRequestQueue = pFlowData->wdfUdpResponcePermitQueue;
    ASSERT(NULL != wdfRequestQueue);
    pFlowData->wdfUdpResponcePermitQueue = NULL;
    status = WdfIoQueueRetrieveNextRequest(
        wdfRequestQueue,
        &wdfPendedRequest
        );
    ASSERT(NT_SUCCESS(status));
    WdfRequestCompleteWithInformation(
        wdfPendedRequest,
        status,
        sizeof(UDP_QUERY_APPROVAL)
        );
    pFlowData->enContextState = csUdpFlowDone;
    push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
    WdfObjectDelete(wdfRequestQueue);
    WdfObjectReleaseLock(wdfDeviceObject);
    if (pFlowData->bIPv6)
    {
        status = FwpsFlowRemoveContext(
            pFlowData->flowHandle,
            FWPS_LAYER_DATAGRAM_DATA_V6,
            g_nDatagramIdV6
            );
    }
    else
    {
        status = FwpsFlowRemoveContext(
            pFlowData->flowHandle,
            FWPS_LAYER_DATAGRAM_DATA_V4,
            g_nDatagramId
            );
    }
    ASSERT(NT_SUCCESS(status));
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI allocatedDatagramDnsResponceCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject = NULL;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    WSACMSGHDR*     _pControlData;
    PMDL            _pMdl;
    WDFDEVICE       wdfDeviceObject;
    WDFQUEUE        wdfRequestQueue;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)");
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    ASSERT(csUdpWaitReplaceResponceDone == pFlowData->enContextState);
    ASSERT(pFlowData->pUdpResponceReplaceBufferList == netBufferList);
    FwpsFreeNetBufferList(
        netBufferList
        );
    pFlowData->pUdpResponceReplaceBufferList = NULL;
    _pControlData = pFlowData->stUdpResponceSendParams.controlData;
    if(NULL != _pControlData)
    {
        ExFreePoolWithTag(
            _pControlData,
            POOL_TAG
            );
    }
    RtlZeroMemory(
        &pFlowData->stUdpResponceSendParams,
        sizeof(FWPS_TRANSPORT_SEND_PARAMS)
        );
    _pMdl = pFlowData->pUdpResponceMdl;
    pFlowData->pUdpResponceMdl = NULL;
    ASSERT(NULL != _pMdl);
    IoFreeMdl(_pMdl);
    ExFreePoolWithTag(
        _pMdl->MappedSystemVa,
        POOL_TAG
        );
    wdfRequestQueue = pFlowData->wdfUdpResponceReplaceQueue;
    ASSERT(NULL != wdfRequestQueue);
    pFlowData->wdfUdpResponceReplaceQueue = NULL;
    status = WdfIoQueueRetrieveNextRequest(
        wdfRequestQueue,
        &wdfPendedRequest
        );
    ASSERT(NT_SUCCESS(status));
    WdfRequestCompleteWithInformation(
        wdfPendedRequest,
        status,
        pFlowData->ulUdpResponceReplaceDataSize
        );
    pFlowData->ulUdpResponceReplaceDataSize = 0;
    pFlowData->enContextState = csUdpFlowDone;
    push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
    WdfObjectDelete(wdfRequestQueue);
    WdfObjectReleaseLock(wdfDeviceObject);
    if (pFlowData->bIPv6)
    {
        status = FwpsFlowRemoveContext(
            pFlowData->flowHandle,
            FWPS_LAYER_DATAGRAM_DATA_V6,
            g_nDatagramIdV6
            );
    }
    else
    {
        status = FwpsFlowRemoveContext(
            pFlowData->flowHandle,
            FWPS_LAYER_DATAGRAM_DATA_V4,
            g_nDatagramId
            );
    }
    ASSERT(NT_SUCCESS(status));
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI onStreamClassify(
    __in const FWPS_INCOMING_VALUES* inFixedValues,
    __in const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
    __inout_opt void* layerData,
    _In_opt_ const void* classifyContext,
    __in const FWPS_FILTER* filter,
    __in UINT64 flowContext,
    __out FWPS_CLASSIFY_OUT* classifyOut
    )
{
    FWP_DIRECTION                   enPacketDirection;
    WDFFILEOBJECT                   wdfFlowFileObject = NULL;
    PFLOW_DATA                      pFlowData;
    BOOLEAN                         bInbound;
    BOOLEAN                         bIPv6;
    FWPS_STREAM_CALLOUT_IO_PACKET*  pStreamPacket;
    FWPS_STREAM_DATA0*              pStreamBuffer;
    SIZE_T                          nStreamLength;
    UINT64                          hFlowHandle;
    NTSTATUS                        status;
    WDFREQUEST                      wdfPendedRequest;
    WDFMEMORY                       wdfOutputMemory;
    UINT16                          wMessageLength;
    SIZE_T                          nBytesCopied;
    WDFDEVICE                       wdfDeviceObject;
    WDFQUEUE                        wdfRequestQueue;
    PNET_BUFFER_LIST                pNetBufferList = NULL;
    PMDL                            _pMdl;
    UNREFERENCED_PARAMETER(classifyContext);
    UNREFERENCED_PARAMETER(filter);
    UNREFERENCED_PARAMETER(inFixedValues);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Entry"
        );
    //
    // We don't have the necessary right to alter the classify, exit.
    //
    if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0)
    {
        //
        // Cannot alter the action.
        //
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))"
            );
        return;
    }
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    pStreamPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData;
    ASSERT(NULL != pStreamPacket);
    pStreamBuffer = pStreamPacket->streamData;
    nStreamLength = pStreamBuffer->dataLength;
    pFlowData = (PFLOW_DATA)flowContext;
    ASSERT(NULL != pFlowData);
    ASSERT(pFlowData == pFlowData->pThis);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    bIPv6 = pFlowData->bIPv6;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! Stream is IPv6: %!bool!",
        bIPv6
        );
    bInbound = (BOOLEAN) ((pStreamBuffer->flags & FWPS_STREAM_FLAG_RECEIVE) == FWPS_STREAM_FLAG_RECEIVE);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Stream is INBOUND: %!bool!",
        bInbound
        );
    enPacketDirection = (bInbound) ? FWP_DIRECTION_INBOUND : FWP_DIRECTION_OUTBOUND;
    pFlowData->enPacketDirection = enPacketDirection;
    ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, 
                                          FWPS_METADATA_FIELD_FLOW_HANDLE));
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (0 == nStreamLength)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (0 == nStreamLength) enContextState = %u",
            pFlowData->enContextState
            );
        classifyOut->actionType = FWP_ACTION_PERMIT;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    hFlowHandle = inMetaValues->flowHandle;
    ASSERT(hFlowHandle == pFlowData->flowHandle);
    switch (pFlowData->enContextState)
    {
    case csTcpFlowEstablished:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpFlowEstablished\""
            );
        goto _tcp_query_elaborate;
    case csTcpQueryMoreData01:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData01\""
            );
        goto _tcp_query_elaborate;
    case csTcpQueryMoreData02:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData02\""
            );
    _tcp_query_elaborate:
        ASSERT(bInbound);
        pFlowData->hTcpQueryFlowID = hFlowHandle;
        ASSERT(nStreamLength >= sizeof(UINT16));
        FwpsCopyStreamDataToBuffer(
            pStreamBuffer,
            &wMessageLength,
            sizeof(wMessageLength),
            &nBytesCopied
            );
        ASSERT(nBytesCopied == sizeof(wMessageLength));
        wMessageLength = RtlUshortByteSwap(wMessageLength);
        if(nStreamLength < (SIZE_T)wMessageLength + sizeof(wMessageLength))
        {
            classifyOut->actionType = FWP_ACTION_BLOCK;
            classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
            classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
            pStreamPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;
            pStreamPacket->countBytesRequired = (UINT32)wMessageLength + sizeof(wMessageLength);
            switch (pFlowData->enContextState)
            {
            case csTcpWaitPermitResponce:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData02;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpWaitReplaceResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData02;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpQueryMoreData02:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData02\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData02;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpPermitResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData02;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpReplaceResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData02;
                push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
                break;
            case csTcpFlowEstablished:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpFlowEstablished\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData01;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpQueryMoreData01:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData01\""
                    );
                pFlowData->enContextState = csTcpQueryMoreData01;
                push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit (switch (pFlowData->enContextState) default:) enContextState = %u",
                    pFlowData->enContextState
                    );
                pFlowData->enContextState = csTcpQueryMoreData01;
                push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
                break;
            }
            break;
        }
        ASSERT((SIZE_T)wMessageLength + 2 == nStreamLength);
        switch (pFlowData->enContextState)
        {
        case csTcpPermitResponceDone:
        case csTcpReplaceResponceDone:
        case csTcpWaitPermitResponce:
        case csTcpWaitReplaceResponceDone:
        case csTcpQueryMoreData02:
            wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
            break;
        case csTcpFlowEstablished:
        case csTcpQueryMoreData01:
            wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Exit (switch (pFlowData->enContextState) default:) enContextState = %u",
                pFlowData->enContextState
                );
            wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
            break;
        }
        ASSERT(NULL != wdfRequestQueue);
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfRequestRetrieveOutputMemory(
            wdfPendedRequest,
            &wdfOutputMemory
            );
        ASSERT(NT_SUCCESS(status));
        switch (pFlowData->enContextState)
        {
        case csTcpPermitResponceDone:
        case csTcpReplaceResponceDone:
        case csTcpWaitPermitResponce:
        case csTcpWaitReplaceResponceDone:
        case csTcpQueryMoreData02:
            pFlowData->wdfWaitTcpNewMessageQueue = NULL;
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Exit (switch (pFlowData->enContextState) default:) enContextState = %u",
                pFlowData->enContextState
                );
            pFlowData->wdfWaitDnsQueryQueue = NULL;
            break;
        }
        pFlowData->nTcpQueryLength = nStreamLength;
        status = FwpsCloneStreamData(
            pStreamBuffer,
            g_hNetBufferListPool,
            NULL,
            0,
            &pNetBufferList
            );
        ASSERT(NT_SUCCESS(status));
        pFlowData->pTcpQueryBufferList = pNetBufferList;
        switch (pFlowData->enContextState)
        {
        case csTcpWaitPermitResponce:
        case csTcpWaitReplaceResponceDone:
        case csTcpQueryMoreData02:
            pFlowData->enContextState = csTcpWaitResponceInjectDone;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        case csTcpFlowEstablished:
        case csTcpQueryMoreData01:
        case csTcpPermitResponceDone:
        case csTcpReplaceResponceDone:
            pFlowData->enContextState = csTcpWaitReadQuery;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_WARNING,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Exit (switch (pFlowData->enContextState) default:) enContextState = %u",
                pFlowData->enContextState
                );
            pFlowData->enContextState = csTcpWaitReadQuery;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        }
        status = WdfMemoryCopyFromBuffer(
            wdfOutputMemory,
            0,
            pFlowData,
            sizeof(FLOW_DATA)
            );
        ASSERT(NT_SUCCESS(status));
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            sizeof(FLOW_DATA)
            );
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        WdfObjectDelete(wdfRequestQueue);
        break;
    case csTcpPermitQueryDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
            );
        goto _tcp_responce_elaborate;
    case csTcpReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryDone\""
            );
        goto _tcp_responce_elaborate;
    case csTcpResponceMoreData01:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData01\""
            );
        goto _tcp_responce_elaborate;
    case csTcpWaitPermitQuery:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitQuery\""
            );
        goto _tcp_responce_elaborate;
    case csTcpWaitReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceQueryDone\""
            );
        goto _tcp_responce_elaborate;
    case csTcpResponceMoreData02:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
            );
        goto _tcp_responce_elaborate;
    case csTcpResponceMoreData03:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
            );
    _tcp_responce_elaborate:
        switch (pFlowData->enContextState)
        {
        case csTcpPermitResponceDone:
        case csTcpReplaceResponceDone:
        case csTcpWaitPermitResponce:
        case csTcpWaitReplaceResponceDone:
        case csTcpResponceMoreData03:
            wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
            break;
        default:
            wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
            break;
        }
        ASSERT(NULL != wdfRequestQueue);
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        status = WdfRequestRetrieveOutputMemory(
            wdfPendedRequest,
            &wdfOutputMemory
            );
        ASSERT(NT_SUCCESS(status));
        if(pStreamBuffer->flags&FWPS_STREAM_FLAG_SEND)
        {
            bInbound = FALSE;
            enPacketDirection = FWP_DIRECTION_OUTBOUND;
            pFlowData->enPacketDirection = enPacketDirection;
        }
        else
        {
            NTSTATUS    status1;
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid flow direction %!STATUS!",
                status
                );
            classifyOut->actionType = FWP_ACTION_BLOCK;
            classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
            switch (pFlowData->enContextState)
            {
            case csTcpPermitQueryDone:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
                    );
                break;
            case csTcpReplaceQueryDone:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryDone\""
                    );
                break;
            case csTcpResponceMoreData01:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData01\""
                    );
                break;
            case csTcpWaitPermitQuery:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitQuery\""
                    );
                pNetBufferList = pFlowData->pTcpQueryBufferList;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeCloneNetBufferList(
                        pNetBufferList,
                        0
                        );
                    pFlowData->pTcpQueryBufferList = NULL;
                }
                break;
            case csTcpWaitReplaceQueryDone:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceQueryDone\""
                    );
                pNetBufferList = pFlowData->pTcpQueryInjectBufferList;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeNetBufferList(
                        pNetBufferList
                        );
                    pFlowData->pTcpQueryInjectBufferList = NULL;
                    _pMdl = pFlowData->pTcpQueryMdl;
                    ASSERT(NULL != _pMdl);
                    IoFreeMdl(_pMdl);
                    ExFreePoolWithTag(
                        _pMdl->MappedSystemVa,
                        POOL_TAG
                        );
                    pFlowData->pTcpQueryMdl = NULL;
                }
                break;
            case csTcpResponceMoreData02:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
                    );
                break;
            case csTcpResponceMoreData03:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData03\""
                    );
                break;
            case csTcpPermitResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                    );
                break;
            case csTcpReplaceResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                    );
                break;
            case csTcpWaitPermitResponce:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
                    );
                pNetBufferList = pFlowData->pTcpResponceBufferList;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeCloneNetBufferList(
                        pNetBufferList,
                        0
                        );
                    pFlowData->pTcpResponceBufferList = NULL;
                }
                break;
            case csTcpWaitReplaceResponceDone:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
                    );
                pNetBufferList = pFlowData->pTcpResponceInjectBufferList;
                if (NULL != pNetBufferList)
                {
                    FwpsFreeNetBufferList(
                        pNetBufferList
                        );
                    pFlowData->pTcpResponceInjectBufferList = NULL;
                    _pMdl = pFlowData->pTcpResponceMdl;
                    ASSERT(NULL != _pMdl);
                    IoFreeMdl(_pMdl);
                    ExFreePoolWithTag(
                        _pMdl->MappedSystemVa,
                        POOL_TAG
                        );
                    pFlowData->pTcpResponceMdl = NULL;
                }
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine. enContextState = %u",
                    pFlowData->enContextState
                    );
                break;
            }
            WdfObjectReleaseLock(wdfDeviceObject);
            if (bIPv6)
            {
                status = FwpsFlowRemoveContext(
                    pFlowData->flowHandle,
                    FWPS_LAYER_STREAM_V6,
                    g_nStreamIdV6
                    );
            }
            else
            {
                status = FwpsFlowRemoveContext(
                    pFlowData->flowHandle,
                    FWPS_LAYER_STREAM_V4,
                    g_nStreamId
                    );
            }
            TraceEvents(
                TRACE_LEVEL_WARNING,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! \"FwpsFlowRemoveContext\" return %!STATUS!",
                status
                );
            if ((STATUS_SUCCESS == status) || (STATUS_PENDING == status))
            {
                return;
            }
            pFlowData->enContextState = csDnsError;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            status1 = status;
            wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
            pFlowData->wdfWaitFlowDoneQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                status = WdfIoQueueRetrieveNextRequest(
                    wdfRequestQueue,
                    &wdfPendedRequest
                    );
                if (NT_SUCCESS(status))
                {
                    status = WdfRequestRetrieveOutputMemory(
                        wdfPendedRequest,
                        &wdfOutputMemory
                        );
                    ASSERT(NT_SUCCESS(status));
                    status = WdfMemoryCopyFromBuffer(
                        wdfOutputMemory,
                        0,
                        pFlowData,
                        sizeof(FLOW_DATA)
                        );
                    ASSERT(NT_SUCCESS(status));
                    WdfRequestCompleteWithInformation(
                        wdfPendedRequest,
                        status1,
                        sizeof(FLOW_DATA)
                        );
                }
                WdfObjectDelete(wdfRequestQueue);
            }
            return;
        }
        pFlowData->hTcpResponceFlowID = hFlowHandle;
        ASSERT(nStreamLength >= sizeof(UINT16));
        FwpsCopyStreamDataToBuffer(
            pStreamBuffer,
            &wMessageLength,
            sizeof(wMessageLength),
            &nBytesCopied
            );
        ASSERT(nBytesCopied == sizeof(wMessageLength));
        wMessageLength = RtlUshortByteSwap(wMessageLength);
        if(nStreamLength < (SIZE_T)wMessageLength + 2)
        {
            classifyOut->actionType = FWP_ACTION_BLOCK;
            classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
            pStreamPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;
            pStreamPacket->countBytesRequired = (UINT32)wMessageLength + 2;
            switch (pFlowData->enContextState)
            {
            case csTcpPermitQueryDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData01;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpReplaceQueryDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryDone\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData01;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpPermitResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData01;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpReplaceResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData01;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpResponceMoreData01:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData01\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData01;
                push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
                break;
            case csTcpWaitPermitQuery:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitQuery\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData02;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpWaitReplaceQueryDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceQueryDone\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData02;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpResponceMoreData02:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData02;
                push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
                break;
            case csTcpWaitPermitResponce:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData03;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpWaitReplaceResponceDone:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData03;
                push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
                break;
            case csTcpResponceMoreData03:
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData03\""
                    );
                pFlowData->enContextState = csTcpResponceMoreData03;
                push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
                break;
            default:
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine. enContextState = %u",
                    pFlowData->enContextState
                    );
                break;
            }
            WdfObjectDelete(wdfRequestQueue);
            break;
        }
        ASSERT((SIZE_T)wMessageLength + sizeof(wMessageLength) == nStreamLength);
        switch (pFlowData->enContextState)
        {
        case csTcpPermitResponceDone:
        case csTcpReplaceResponceDone:
        case csTcpWaitPermitResponce:
        case csTcpWaitReplaceResponceDone:
        case csTcpResponceMoreData03:
            pFlowData->wdfWaitTcpNewMessageQueue = NULL;
            break;
        default:
            pFlowData->wdfWaitTcpResponceQueue = NULL;
            break;
        }
        pFlowData->nTcpResponceLength = nStreamLength;
        status = FwpsCloneStreamData(
            pStreamBuffer,
            g_hNetBufferListPool,
            NULL,
            0,
            &pFlowData->pTcpResponceBufferList
            );
        ASSERT(NT_SUCCESS(status));
        switch (pFlowData->enContextState)
        {
        case csTcpPermitQueryDone:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
                );
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpReplaceQueryDone:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryDone\""
                );
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpResponceMoreData01:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData01\""
                );
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpPermitResponceDone:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                );
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpReplaceResponceDone:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                );
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        case csTcpWaitPermitQuery:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitQuery\""
                );
            pFlowData->enContextState = csTcpWaitQueryInjectDone;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpWaitReplaceQueryDone:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceQueryDone\""
                );
            pFlowData->enContextState = csTcpWaitQueryInjectDone;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpResponceMoreData02:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
                );
            pFlowData->enContextState = csTcpWaitQueryInjectDone;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        case csTcpWaitPermitResponce:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
                );
            pFlowData->enContextState = csTcpWaitResponceInjectDone;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpWaitReplaceResponceDone:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
                );
            pFlowData->enContextState = csTcpWaitResponceInjectDone;
            push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
            break;
        case csTcpResponceMoreData03:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData03\""
                );
            pFlowData->enContextState = csTcpWaitResponceInjectDone;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine. enContextState = %u",
                pFlowData->enContextState
                );
            break;
        }
        status = WdfMemoryCopyFromBuffer(
            wdfOutputMemory,
            0,
            pFlowData,
            sizeof(FLOW_DATA)
            );
        ASSERT(NT_SUCCESS(status));
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            sizeof(FLOW_DATA)
            );
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        WdfObjectDelete(wdfRequestQueue);
        break;
    case csTcpPermitResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
            );
        goto _cont15;
    case csTcpReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
            );
        goto _cont15;
    case csTcpWaitPermitResponce:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
            );
        goto _cont15;
    case csTcpWaitReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
            );
    _cont15:
        if (bInbound)
        {
            goto _tcp_query_elaborate;
        }
        goto _tcp_responce_elaborate;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine. enContextState = %u",
            pFlowData->enContextState
            );
        break;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI clonedStreamDnsQueryCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    WDFDEVICE       wdfDeviceObject;
    WDFQUEUE        wdfRequestQueue;
    int             i = 0;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Entry"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_WARNING,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)"
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    if(NULL != NET_BUFFER_LIST_NEXT_NBL(netBufferList))
    {
        FwpsFreeCloneNetBufferList(
            netBufferList,
            0
            );
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (NULL != NET_BUFFER_LIST_NEXT_NBL(netBufferList))"
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    else
    {
        FwpsFreeCloneNetBufferList(
            netBufferList,
            0
            );
    }
    pFlowData->pTcpQueryBufferList = NULL;
    pFlowData->hTcpQueryFlowID = 0;
    wdfRequestQueue = pFlowData->wdfTcpQueryPermitQueue;
    if(NULL != wdfRequestQueue)
    {
        pFlowData->wdfTcpQueryPermitQueue = NULL;
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            sizeof(TCP_QUERY_APPROVAL)
            );
        WdfObjectDelete(wdfRequestQueue);
    }
    switch(pFlowData->arContextStateStack[i])
    {
    case csTcpWaitPermitQuery:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitQuery\""
            );
        goto _cont13;
    case csTcpPermitQueryDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
            );
    _cont13:
        pFlowData->enContextState = csTcpPermitQueryDone;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csTcpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitQueryInjectDone\""
            );
        pFlowData->enContextState = csTcpWaitReadResponce;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csDnsError:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csDnsError\""
            );
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        break;
    case csTcpResponceMoreData02:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
            );
        pFlowData->enContextState = csTcpResponceMoreData01;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI allocatedStreamDnsQueryCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    PMDL            _pMdl;
    ULONG_PTR       Information;
    WDFDEVICE       wdfDeviceObject;
    WDFQUEUE        wdfRequestQueue;
    int             i = 0;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Entry"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_VERBOSE,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)"
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    ASSERT(pFlowData->pTcpQueryInjectBufferList == netBufferList);
    FwpsFreeNetBufferList(
        netBufferList
        );
    pFlowData->pTcpQueryInjectBufferList = NULL;
    _pMdl = pFlowData->pTcpQueryMdl;
    ASSERT(NULL != _pMdl);
    IoFreeMdl(_pMdl);
    ExFreePoolWithTag(
        _pMdl->MappedSystemVa,
        POOL_TAG
        );
    pFlowData->pTcpQueryMdl = NULL;
    wdfRequestQueue = pFlowData->wdfTcpQueryInjectQueue;
    pFlowData->wdfTcpQueryInjectQueue = NULL;
    if(NULL != wdfRequestQueue)
    {
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        Information = pFlowData->nTcpQueryInjectDataSize;
        pFlowData->nTcpQueryInjectDataSize = 0;
        ASSERT(0 != Information);
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            Information
            );
        WdfObjectDelete(wdfRequestQueue);
    }
    switch(pFlowData->arContextStateStack[i])
    {
    case csTcpWaitReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceQueryDone\""
            );
        pFlowData->enContextState = csTcpReplaceQueryDone;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csTcpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitQueryInjectDone\""
            );
        pFlowData->enContextState = csTcpWaitReadResponce;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csDnsError:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csDnsError\""
            );
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        break;
    case csTcpResponceMoreData02:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
            );
        pFlowData->enContextState = csTcpResponceMoreData01;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI clonedStreamDnsResponceCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    BOOLEAN         bInbound;
    WDFDEVICE       wdfDeviceObject;
    WDFQUEUE        wdfRequestQueue;
    int             i = 0;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Entry"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)"
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    bInbound = (BOOLEAN)(FWP_DIRECTION_INBOUND == pFlowData->enPacketDirection);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Stream is INBOUND: %!bool!",
        bInbound
        );
    if (NULL != NET_BUFFER_LIST_NEXT_NBL(netBufferList))
    {
        FwpsFreeCloneNetBufferList(
            netBufferList,
            0
            );
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (NULL != NET_BUFFER_LIST_NEXT_NBL(netBufferList))"
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    else
    {
        FwpsFreeCloneNetBufferList(
            netBufferList,
            0
            );
    }
    pFlowData->pTcpResponceBufferList = NULL;
    wdfRequestQueue = pFlowData->wdfTcpResponcePermitQueue;
    pFlowData->wdfTcpResponcePermitQueue = NULL;
    if(NULL != wdfRequestQueue)
    {
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            sizeof(TCP_RESPONCE_APPROVAL)
            );
        WdfObjectDelete(wdfRequestQueue);
    }
    switch(pFlowData->arContextStateStack[i])
    {
    case csTcpPermitResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
            );
        goto _cont14;
    case csTcpWaitPermitResponce:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
            );
    _cont14:
        pFlowData->enContextState = csTcpPermitResponceDone;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csTcpWaitResponceInjectDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitResponceInjectDone\"; bInbound = %!bool!",
            bInbound
            );
        if (bInbound)
        {
            pFlowData->enContextState = csTcpWaitReadQuery;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        }
        else
        {
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        }
        break;
    case csTcpQueryMoreData02:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData02\""
            );
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI allocatedStreamDnsResponceCompletionFn(
  __in     VOID *context,
  __inout  NET_BUFFER_LIST *netBufferList,
  __in     BOOLEAN dispatchLevel
)
{
    WDFFILEOBJECT   wdfFlowFileObject;
    PFLOW_DATA      pFlowData;
    WDFREQUEST      wdfPendedRequest;
    NTSTATUS        status = STATUS_SUCCESS;
    PMDL            _pMdl;
    ULONG_PTR       Information;
    BOOLEAN         bInbound;
    WDFDEVICE       wdfDeviceObject;
    WDFQUEUE        wdfRequestQueue;
    int             i = 0;
    UNREFERENCED_PARAMETER(dispatchLevel);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Entry"
        );
    pFlowData = (PFLOW_DATA) context;
    ASSERT(NULL != pFlowData);
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    if (csFlowDeleted == pFlowData->enContextState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (csFlowDeleted == pFlowData->enContextState)"
            );
        WdfObjectReleaseLock(wdfDeviceObject);
        return;
    }
    ASSERT(pFlowData == pFlowData->pThis);
    bInbound = (BOOLEAN)(FWP_DIRECTION_INBOUND == pFlowData->enPacketDirection);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Stream is INBOUND: %!bool!",
        bInbound
        );
    ASSERT(pFlowData->pTcpResponceInjectBufferList == netBufferList);
    FwpsFreeNetBufferList(
        netBufferList
        );
    pFlowData->pTcpResponceInjectBufferList = NULL;
    _pMdl = pFlowData->pTcpResponceMdl;
    ASSERT(NULL != _pMdl);
    IoFreeMdl(_pMdl);
    ExFreePoolWithTag(
        _pMdl->MappedSystemVa,
        POOL_TAG
        );
    pFlowData->pTcpResponceMdl = NULL;
    wdfRequestQueue = pFlowData->wdfTcpResponceInjectQueue;
    pFlowData->wdfTcpResponceInjectQueue = NULL;
    if(NULL != wdfRequestQueue)
    {
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        ASSERT(NT_SUCCESS(status));
        Information = pFlowData->nTcpResponceInjectDataSize;
        pFlowData->nTcpResponceInjectDataSize = 0;
        ASSERT(0 != Information);
        WdfRequestCompleteWithInformation(
            wdfPendedRequest,
            status,
            Information
            );
        WdfObjectDelete(wdfRequestQueue);
    }
    switch(pFlowData->arContextStateStack[i])
    {
    case csTcpWaitReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
            );
        pFlowData->enContextState = csTcpReplaceResponceDone;
        push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        break;
    case csTcpWaitResponceInjectDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitResponceInjectDone\""
            );
        if (bInbound)
        {
            pFlowData->enContextState = csTcpWaitReadQuery;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        }
        else
        {
            pFlowData->enContextState = csTcpWaitReadResponce;
            push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
        }
        break;
    case csTcpQueryMoreData02:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData02\""
            );
        push_css(pFlowData->arContextStateStack, pFlowData->enContextState);
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void NTAPI onDnsFlowDeletion(
    __in UINT16 layerId,
    __in UINT32 calloutId,
    __in UINT64 flowContext
    )
{
    WDFFILEOBJECT       wdfFlowFileObject;
    PFLOW_DATA          pFlowData;
    WDFREQUEST          wdfPendedRequest = NULL;
    NTSTATUS            status = STATUS_SUCCESS;
    WDFMEMORY           wdfOutputMemory;
    BOOLEAN             bStateFinded = FALSE;
    int                 i, j, nQueueIndx = 0;
    WDFDEVICE           wdfDeviceObject;
    WDFQUEUE            wdfRequestQueue = NULL;
    WDFQUEUE            arRequestQueue[16] = { NULL };
    BOOLEAN             bListEmpty;
    PNET_BUFFER_LIST    pNetBufferList;
    PMDL                _pMdl;
    UNREFERENCED_PARAMETER(layerId);
    UNREFERENCED_PARAMETER(calloutId);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Entry"
        );
    pFlowData = (PFLOW_DATA) flowContext;
    if(NULL == pFlowData)
    {
        TraceEvents(
            TRACE_LEVEL_WARNING,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (NULL == pFlowData)"
            );
        return;
    }
    if (NULL == pFlowData->pThis)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (NULL == pFlowData->pThis)"
            );
        return;
    }
    if (pFlowData != pFlowData->pThis)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (pFlowData != pFlowData->pThis)"
            );
        return;
    }
    if(0 == pFlowData->flowHandle)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (0 == pFlowData->flowHandle)"
            );
        return;
    }
    wdfFlowFileObject = pFlowData->wdfFlowFileObject;
    ASSERT(NULL != wdfFlowFileObject);
    wdfDeviceObject = g_wdfControlDevice;
    WdfObjectAcquireLock(wdfDeviceObject);
    j = 1;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! State number of the State machine is %d",
        pFlowData->enContextState
        );
    pFlowData->enContextState = csFlowDeleted;
    push_css(pFlowData->arContextStateStack,pFlowData->enContextState);
    switch (pFlowData->arContextStateStack[j])
    {
    case csTcpReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
            );
        goto _cont06;
    case csTcpPermitResponceDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
            );
    _cont06:
        wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
        pFlowData->wdfWaitTcpNewMessageQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpReplaceResponcePended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponcePended\""
            );
        bListEmpty = RemoveEntryList(&pFlowData->threadListEntry);
        goto _cont07;
    case csTcpWaitReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
            );
    _cont07:
        pNetBufferList = pFlowData->pTcpResponceInjectBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsFreeNetBufferList(
                pNetBufferList
                );
            pFlowData->pTcpResponceInjectBufferList = NULL;
            _pMdl = pFlowData->pTcpResponceMdl;
            ASSERT(NULL != _pMdl);
            IoFreeMdl(_pMdl);
            ExFreePoolWithTag(
                _pMdl->MappedSystemVa,
                POOL_TAG
                );
            pFlowData->pTcpResponceMdl = NULL;
        }
        wdfRequestQueue = pFlowData->wdfTcpResponceInjectQueue;
        pFlowData->wdfTcpResponceInjectQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
        pFlowData->wdfWaitTcpNewMessageQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpWaitResponceInjectDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitResponceInjectDone\""
            );
        pNetBufferList = pFlowData->pTcpResponceInjectBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsFreeNetBufferList(
                pNetBufferList
                );
            pFlowData->pTcpResponceInjectBufferList = NULL;
            _pMdl = pFlowData->pTcpResponceMdl;
            ASSERT(NULL != _pMdl);
            IoFreeMdl(_pMdl);
            ExFreePoolWithTag(
                _pMdl->MappedSystemVa,
                POOL_TAG
                );
            pFlowData->pTcpResponceMdl = NULL;
        }
        wdfRequestQueue = pFlowData->wdfTcpResponceInjectQueue;
        pFlowData->wdfTcpResponceInjectQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpWaitNewMessageStarted:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitNewMessageStarted\""
            );
        pNetBufferList = pFlowData->pTcpResponceBufferList;
        pFlowData->pTcpResponceBufferList = NULL;
        if (NULL != pNetBufferList)
        {
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
        }
        wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
        pFlowData->wdfWaitTcpNewMessageQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpResponceDiscarded:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceDiscarded\""
            );
        break;
    case csTcpWaitReplaceResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponce\""
            );
        break;
    case csTcpPermitResponcePended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponcePended\""
            );
        bListEmpty = RemoveEntryList(&pFlowData->threadListEntry);
        goto _cont08;
    case csTcpWaitPermitResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponcePended\""
            );
    _cont08:
        pNetBufferList = pFlowData->pTcpResponceBufferList;
        pFlowData->pTcpResponceBufferList = NULL;
        if (NULL != pNetBufferList)
        {
            FwpsFreeCloneNetBufferList(
                pNetBufferList,
                0
                );
        }
        wdfRequestQueue = pFlowData->wdfTcpResponcePermitQueue;
        pFlowData->wdfTcpResponcePermitQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
        pFlowData->wdfWaitTcpNewMessageQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpWaitApprovalResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitApprovalResponce\""
            );
        pNetBufferList = pFlowData->pTcpResponceBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowData->pTcpResponceBufferList = NULL;
        }
        break;
    case csTcpWaitReadResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReadResponce\""
            );
        pNetBufferList = pFlowData->pTcpResponceBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowData->pTcpResponceBufferList = NULL;
        }
        break;
    case csTcpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitQueryInjectDone\""
            );
        pNetBufferList = pFlowData->pTcpResponceBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
            pFlowData->pTcpResponceBufferList = NULL;
        }
        break;
    case csTcpResponceMoreData03:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData03\""
            );
        wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
        pFlowData->wdfWaitTcpNewMessageQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpResponceMoreData02:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
            );
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpResponceMoreData01:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData01\""
            );
        switch (pFlowData->arContextStateStack[j+1])
        {
        case csTcpPermitQueryDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
                );
            goto _cont09;
        case csTcpReplaceQueryDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryDone\""
                );
            goto _cont09;
        case csTcpResponceMoreData01:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData01\""
                );
            goto _cont09;
        case csTcpResponceMoreData02:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpResponceMoreData02\""
                );
        _cont09:
            wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
            pFlowData->wdfWaitTcpResponceQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        case csTcpPermitResponceDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                );
            goto _cont10;
        case csTcpReplaceResponceDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                );
        _cont10:
            wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
            pFlowData->wdfWaitTcpNewMessageQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
            pFlowData->wdfWaitTcpResponceQueue = NULL;
            break;
        }
        break;
    case csTcpReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryDone\""
            );
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpReplaceQueryPended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryPended\""
            );
        bListEmpty = RemoveEntryList(&pFlowData->threadListEntry);
        goto _cont11;
    case csTcpWaitReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceQueryDone\""
            );
    _cont11:
        pNetBufferList = pFlowData->pTcpQueryInjectBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsFreeNetBufferList(
                pNetBufferList
                );
            pFlowData->pTcpQueryInjectBufferList = NULL;
            _pMdl = pFlowData->pTcpQueryMdl;
            ASSERT(NULL != _pMdl);
            IoFreeMdl(_pMdl);
            ExFreePoolWithTag(
                _pMdl->MappedSystemVa,
                POOL_TAG
                );
            pFlowData->pTcpQueryMdl = NULL;
        }
        wdfRequestQueue = pFlowData->wdfTcpQueryInjectQueue;
        pFlowData->wdfTcpQueryInjectQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpPermitQueryDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryDone\""
            );
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpPermitQueryPended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryPended\""
            );
        bListEmpty = RemoveEntryList(&pFlowData->threadListEntry);
        goto _cont12;
    case csTcpWaitPermitQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitQuery\""
            );
    _cont12:
        pNetBufferList = pFlowData->pTcpQueryBufferList;
        if (NULL != pNetBufferList)
        {
            FwpsFreeCloneNetBufferList(
                pNetBufferList,
                0
                );
            pFlowData->pTcpQueryBufferList = NULL;
        }
        wdfRequestQueue = pFlowData->wdfTcpQueryPermitQueue;
        pFlowData->wdfTcpQueryPermitQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpQueryDiscarded:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryDiscarded\" or \"csTcpWaitReplaceQuery\""
            );
        break;
    case csTcpWaitReplaceQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryDiscarded\" or \"csTcpWaitReplaceQuery\""
            );
        break;
    case csTcpWaitResponceStarted:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitResponceStarted\""
            );
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpWaitApprovalQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitApprovalQuery\""
            );
        pNetBufferList = pFlowData->pTcpQueryBufferList;
        pFlowData->pTcpQueryBufferList = NULL;
        if (NULL != pNetBufferList)
        {
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
        }
        break;
    case csTcpWaitReadQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReadQuery\""
            );
        pNetBufferList = pFlowData->pTcpQueryBufferList;
        pFlowData->pTcpQueryBufferList = NULL;
        if (NULL != pNetBufferList)
        {
            FwpsDiscardClonedStreamData(
                pNetBufferList,
                0,
                FALSE
                );
        }
        break;
    case csTcpQueryMoreData02:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData02\""
            );
        switch (pFlowData->arContextStateStack[j + 1])
        {
        case csTcpQueryMoreData02:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData02\""
                );
            for (i = j + 2; i<20; i++)
            {
                if(csTcpQueryMoreData02 != pFlowData->arContextStateStack[i])
                {
                    bStateFinded = TRUE;
                    break;
                }
            }
            if(!bStateFinded)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid stack of State machine"
                    );
                break;
            }
            if(csTcpWaitReplaceResponceDone == pFlowData->arContextStateStack[i])
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
                    );
                goto _elaborate_replace_responce_01;
            }
            else if(csTcpWaitPermitResponce == pFlowData->arContextStateStack[i])
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
                    );
                goto _elaborate_permit_responce_01;
            }
            else
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                    );
                break;
            }
        case csTcpWaitReplaceResponceDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitReplaceResponceDone\""
                );
        _elaborate_replace_responce_01:
            pNetBufferList = pFlowData->pTcpResponceInjectBufferList;
            if (NULL != pNetBufferList)
            {
                FwpsFreeNetBufferList(
                    pNetBufferList
                    );
                pFlowData->pTcpResponceInjectBufferList = NULL;
                _pMdl = pFlowData->pTcpResponceMdl;
                ASSERT(NULL != _pMdl);
                IoFreeMdl(_pMdl);
                ExFreePoolWithTag(
                    _pMdl->MappedSystemVa,
                    POOL_TAG
                    );
                pFlowData->pTcpResponceMdl = NULL;
            }
            wdfRequestQueue = pFlowData->wdfTcpResponceInjectQueue;
            pFlowData->wdfTcpResponceInjectQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        case csTcpWaitPermitResponce:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpWaitPermitResponce\""
                );
        _elaborate_permit_responce_01:
            pNetBufferList = pFlowData->pTcpResponceBufferList;
            if (NULL != pNetBufferList)
            {
                FwpsFreeCloneNetBufferList(
                    pNetBufferList,
                    0
                    );
                pFlowData->pTcpResponceBufferList = NULL;
            }
            wdfRequestQueue = pFlowData->wdfTcpResponcePermitQueue;
            pFlowData->wdfTcpResponcePermitQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
        wdfRequestQueue = pFlowData->wdfWaitTcpResponceQueue;
        pFlowData->wdfWaitTcpResponceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csTcpQueryMoreData01:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData01\""
            );
        switch (pFlowData->arContextStateStack[j + 1])
        {
        case csTcpQueryMoreData01:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpQueryMoreData01\""
                );
            for (i = j + 2; i<20; i++)
            {
                if(csTcpQueryMoreData01 != pFlowData->arContextStateStack[i])
                {
                    bStateFinded = TRUE;
                    break;
                }
            }
            if(!bStateFinded)
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid stack of State machine"
                    );
                break;
            }
            if (csTcpPermitResponceDone == pFlowData->arContextStateStack[i])
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                    );
                goto _elaborate_responce_01;
            }
            if (csTcpReplaceResponceDone == pFlowData->arContextStateStack[i])
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                    );
                goto _elaborate_responce_01;
            }
            else if(csTcpFlowEstablished == pFlowData->arContextStateStack[i])
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpFlowEstablished\""
                    );
                goto _elaborate_query_01;
            }
            else
            {
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                    );
                break;
            }
        case csTcpReplaceResponceDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponceDone\""
                );
            goto _elaborate_responce_01;
        case csTcpPermitResponceDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponceDone\""
                );
        _elaborate_responce_01:
            wdfRequestQueue = pFlowData->wdfWaitTcpNewMessageQueue;
            pFlowData->wdfWaitTcpNewMessageQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        case csTcpFlowEstablished:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpFlowEstablished\""
                );
        _elaborate_query_01:
            wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
            pFlowData->wdfWaitDnsQueryQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
        break;
    case csTcpFlowEstablished:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpFlowEstablished\""
            );
        goto _cont01;
    case csUdpFlowEstablished:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpFlowEstablished\""
            );
    _cont01:
        wdfRequestQueue = pFlowData->wdfWaitDnsQueryQueue;
        pFlowData->wdfWaitDnsQueryQueue = NULL;
        if(NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csUdpFlowDone:
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpFlowDone\""
            );
        break;
    case csUdpReplaceResponcePended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpReplaceResponcePended\""
            );
        bListEmpty = RemoveEntryList(&pFlowData->threadListEntry);
        goto _cont05;
    case csUdpWaitReplaceResponceDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReplaceResponceDone\""
            );
    _cont05:
        wdfRequestQueue = pFlowData->wdfUdpResponceReplaceQueue;
        pFlowData->wdfUdpResponceReplaceQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csUdpWaitReplaceResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReplaceResponce\""
            );
        break;
    case csUdpWaitApprovalResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitApprovalResponce\""
            );
        break;
    case csUdpWaitReadResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitApprovalResponce\""
            );
        break;
    case csUdpPermitResponcePended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpPermitResponcePended\""
            );
        bListEmpty = RemoveEntryList(&pFlowData->threadListEntry);
        goto _cont04;
    case csUdpWaitPermitResponce:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitPermitResponce\""
            );
    _cont04:
        wdfRequestQueue = pFlowData->wdfUdpResponcePermitQueue;
        pFlowData->wdfUdpResponcePermitQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csUdpWaitQueryInjectDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitQueryInjectDone\""
            );
        switch (pFlowData->arContextStateStack[j+1])
        {
        case csUdpWaitReplaceQueryDone:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReplaceQueryDone\""
                );
            wdfRequestQueue = pFlowData->wdfUdpQueryReplaceQueue;
            pFlowData->wdfUdpQueryReplaceQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        case csUdpWaitPermitQuery:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitPermitQuery\""
                );
            wdfRequestQueue = pFlowData->wdfUdpQueryPermitQueue;
            pFlowData->wdfUdpQueryPermitQueue = NULL;
            if (NULL != wdfRequestQueue)
            {
                arRequestQueue[nQueueIndx] = wdfRequestQueue;
                nQueueIndx++;
                ASSERT(nQueueIndx < 16);
            }
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
        break;
    case csUdpQueryDiscarded:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpQueryDiscarded\""
            );
        break;
    case csUdpWaitReplaceQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReplaceQuery\""
            );
        break;
    case csUdpWaitApprovalQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitApprovalQuery\""
            );
        break;
    case csUdpWaitResponceStarted:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitResponceStarted\""
            );
        wdfRequestQueue = pFlowData->wdfWaitUdpResponceQueue;
        pFlowData->wdfWaitUdpResponceQueue = NULL;
        if(NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csUdpWaitReadQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReadQuery\""
            );
        wdfRequestQueue = pFlowData->wdfWaitFlowEstablishedQueue;
        pFlowData->wdfWaitFlowEstablishedQueue = NULL;
        if (NULL != wdfRequestQueue)
        {
            arRequestQueue[nQueueIndx] = wdfRequestQueue;
            nQueueIndx++;
            ASSERT(nQueueIndx < 16);
        }
        break;
    case csUdpReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpReplaceQueryDone\""
            );
        break;
    case csUdpPermitQueryDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpPermitQueryDone\""
            );
        break;
    case csUdpWaitPermitQuery:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitPermitQuery\""
            );
        break;
    case csUdpPermitQueryPended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpPermitQueryPended\""
            );
        break;
    case csUdpWaitReplaceQueryDone:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpWaitReplaceQueryDone\""
            );
        break;
    case csUdpReplaceQueryPended:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpReplaceQueryPended\""
            );
        break;
    default:
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
            );
        break;
    }
    wdfRequestQueue = pFlowData->wdfWaitFlowDoneQueue;
    pFlowData->wdfWaitFlowDoneQueue = NULL;
    if (NULL != wdfRequestQueue)
    {
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        if (NT_SUCCESS(status))
        {
            status = WdfRequestRetrieveOutputMemory(
                wdfPendedRequest,
                &wdfOutputMemory
                );
            ASSERT(NT_SUCCESS(status));
            status = WdfMemoryCopyFromBuffer(
                wdfOutputMemory,
                0,
                pFlowData,
                sizeof(FLOW_DATA)
                );
            ASSERT(NT_SUCCESS(status));
            WdfRequestCompleteWithInformation(
                wdfPendedRequest,
                status,
                sizeof(FLOW_DATA)
                );
        }
        WdfObjectDelete(wdfRequestQueue);
    }
    for (i = 0; i < nQueueIndx; i++)
    {
        status = WdfIoQueueRetrieveNextRequest(
            wdfRequestQueue,
            &wdfPendedRequest
            );
        if (NT_SUCCESS(status))
        {
            WdfRequestCompleteWithInformation(
                wdfPendedRequest,
                STATUS_CANCELLED,
                0
                );
        }
        WdfObjectDelete(wdfRequestQueue);
    }
    pFlowData->flowHandle = 0;
    WdfObjectReleaseLock(wdfDeviceObject);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
}

void
dnsfilterWorker(
    __in PVOID  StartContext
    )
{
    PFLOW_DATA          pFlowContext;
    PLIST_ENTRY         pListEntry;
    NTSTATUS            status = STATUS_SUCCESS;
    WDFFILEOBJECT       wdfFlowFileObject;
    PNET_BUFFER_LIST    pNetBufferList;
    StartContext;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    do
    {
        KeWaitForSingleObject(
            &g_oWorkerEvent,
            Executive,
            KernelMode,
            FALSE,
            NULL
            );
        if(g_bDriverUnload)
        {
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSFILTER,
                "%!FUNC! Cycle break (g_bDriverUnload)");
            break;
        }
        WdfObjectAcquireLock(g_wdfControlDevice);
        if(IsListEmpty(&g_stPendedPacketsQueue))
        {
            KeClearEvent(&g_oWorkerEvent);
            WdfObjectReleaseLock(g_wdfControlDevice);
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSFILTER,
                "%!FUNC! Cycle continue (IsListEmpty(&g_stPendedPacketsQueue))");
            continue;
        }
        pListEntry = RemoveHeadList(&g_stPendedPacketsQueue);
        pFlowContext = CONTAINING_RECORD(
            pListEntry,
            FLOW_DATA,
            threadListEntry
            );
        WdfObjectReleaseLock(g_wdfControlDevice);
        wdfFlowFileObject = pFlowContext->wdfFlowFileObject;
        switch(pFlowContext->enContextState)
        {
        case csUdpPermitResponcePended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpPermitResponcePended\""
                );
            {
                pFlowContext->enContextState = csUdpWaitPermitResponce;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pUdpResponceCloneBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsInjectTransportSendAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext, // 
                        pFlowContext->hUdpResponceEndpointHandle,
                        0,
                        &pFlowContext->stUdpResponceSendParams,
                        AF_INET6,
                        pFlowContext->enUdpResponceCompartmentID,
                        pNetBufferList,
                        clonedDatagramDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsInjectTransportSendAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext, // 
                        pFlowContext->hUdpResponceEndpointHandle,
                        0,
                        &pFlowContext->stUdpResponceSendParams,
                        AF_INET,
                        pFlowContext->enUdpResponceCompartmentID,
                        pNetBufferList,
                        clonedDatagramDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csUdpReplaceResponcePended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpReplaceResponcePended\""
                );
            {
                pFlowContext->enContextState = csUdpWaitReplaceResponceDone;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pUdpResponceReplaceBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsInjectTransportSendAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        pFlowContext->hUdpResponceEndpointHandle,
                        0,
                        &pFlowContext->stUdpResponceSendParams,
                        AF_INET6,
                        pFlowContext->enUdpResponceCompartmentID,
                        pNetBufferList,
                        allocatedDatagramDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsInjectTransportSendAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        pFlowContext->hUdpResponceEndpointHandle,
                        0,
                        &pFlowContext->stUdpResponceSendParams,
                        AF_INET,
                        pFlowContext->enUdpResponceCompartmentID,
                        pNetBufferList,
                        allocatedDatagramDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csUdpReplaceQueryPended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpReplaceQueryPended\""
                );
            {
                pFlowContext->enContextState = csUdpWaitReplaceQueryDone;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pUdpQueryReplaceBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsInjectTransportReceiveAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        NULL,
                        0,
                        AF_INET6,
                        pFlowContext->enUdpQueryCompartmentID,
                        pFlowContext->interfaceIndex,
                        pFlowContext->subInterfaceIndex,
                        pNetBufferList,
                        allocatedDatagramDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsInjectTransportReceiveAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        NULL,
                        0,
                        AF_INET,
                        pFlowContext->enUdpQueryCompartmentID,
                        pFlowContext->interfaceIndex,
                        pFlowContext->subInterfaceIndex,
                        pNetBufferList,
                        allocatedDatagramDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csUdpPermitQueryPended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csUdpPermitQueryPended\""
                );
            {
                pFlowContext->enContextState = csUdpWaitPermitQuery;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pUdpQueryCloneBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsInjectTransportReceiveAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        NULL,
                        0,
                        AF_INET6,
                        pFlowContext->enUdpQueryCompartmentID,
                        pFlowContext->interfaceIndex,
                        pFlowContext->subInterfaceIndex,
                        pNetBufferList,
                        clonedDatagramDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsInjectTransportReceiveAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        NULL,
                        0,
                        AF_INET,
                        pFlowContext->enUdpQueryCompartmentID,
                        pFlowContext->interfaceIndex,
                        pFlowContext->subInterfaceIndex,
                        pNetBufferList,
                        clonedDatagramDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csTcpPermitQueryPended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitQueryPended\""
                );
            {
                pFlowContext->enContextState = csTcpWaitPermitQuery;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pTcpQueryBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpQueryFlowID,
                        g_nStreamIdV6,
                        FWPS_LAYER_STREAM_V6,
                        FWPS_STREAM_FLAG_RECEIVE,
                        pNetBufferList,
                        pFlowContext->nTcpQueryLength,
                        clonedStreamDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpQueryFlowID,
                        g_nStreamId,
                        FWPS_LAYER_STREAM_V4,
                        FWPS_STREAM_FLAG_RECEIVE,
                        pNetBufferList,
                        pFlowContext->nTcpQueryLength,
                        clonedStreamDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csTcpReplaceQueryPended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceQueryPended\""
                );
            {
                pFlowContext->enContextState = csTcpWaitReplaceQueryDone;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pTcpQueryInjectBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpQueryFlowID,
                        g_nStreamIdV6,
                        FWPS_LAYER_STREAM_V6,
                        FWPS_STREAM_FLAG_RECEIVE,
                        pNetBufferList,
                        pFlowContext->nTcpQueryInjectDataSize,
                        allocatedStreamDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpQueryFlowID,
                        g_nStreamId,
                        FWPS_LAYER_STREAM_V4,
                        FWPS_STREAM_FLAG_RECEIVE,
                        pNetBufferList,
                        pFlowContext->nTcpQueryInjectDataSize,
                        allocatedStreamDnsQueryCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csTcpPermitResponcePended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpPermitResponcePended\""
                );
            {
                pFlowContext->enContextState = csTcpWaitPermitResponce;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pTcpResponceBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpResponceFlowID,
                        g_nStreamIdV6,
                        FWPS_LAYER_STREAM_V6,
                        FWPS_STREAM_FLAG_SEND,
                        pNetBufferList,
                        pFlowContext->nTcpResponceLength,
                        clonedStreamDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpResponceFlowID,
                        g_nStreamId,
                        FWPS_LAYER_STREAM_V4,
                        FWPS_STREAM_FLAG_SEND,
                        pNetBufferList,
                        pFlowContext->nTcpResponceLength,
                        clonedStreamDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        case csTcpReplaceResponcePended:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! State of the State machine is \"csTcpReplaceResponcePended\""
                );
            {
                pFlowContext->enContextState = csTcpWaitReplaceResponceDone;
                push_css(pFlowContext->arContextStateStack,pFlowContext->enContextState);
                pNetBufferList = pFlowContext->pTcpResponceInjectBufferList;
                if (pFlowContext->bIPv6)
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandleV6,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpResponceFlowID,
                        g_nStreamIdV6,
                        FWPS_LAYER_STREAM_V6,
                        FWPS_STREAM_FLAG_SEND,
                        pNetBufferList,
                        pFlowContext->nTcpResponceInjectDataSize,
                        allocatedStreamDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                else
                {
                    status = FwpsStreamInjectAsync(
                        g_hInjectionHandle,
                        (HANDLE) pFlowContext,
                        0,
                        pFlowContext->hTcpResponceFlowID,
                        g_nStreamId,
                        FWPS_LAYER_STREAM_V4,
                        FWPS_STREAM_FLAG_SEND,
                        pNetBufferList,
                        pFlowContext->nTcpResponceInjectDataSize,
                        allocatedStreamDnsResponceCompletionFn,
                        (HANDLE) pFlowContext
                        );
                }
                ASSERT(NT_SUCCESS(status));
            }
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSFILTER,
                "%!FUNC! line:%!LINE! %!LEVEL! Invalid State Superintendent of State machine"
                );
            break;
        }
#pragma warning(suppress:4127)
    } while(TRUE);
    ASSERT(g_bDriverUnload);
    WdfObjectAcquireLock(g_wdfControlDevice);
    while(!IsListEmpty(&g_stPendedPacketsQueue))
    {
        if(!IsListEmpty(&g_stPendedPacketsQueue))
        {
            pListEntry = RemoveTailList(&g_stPendedPacketsQueue);
        }
    }
    KeSetEvent(
        &g_oWorkerDoneEvent,
        0,
        FALSE
        );
    WdfObjectReleaseLock(g_wdfControlDevice);
    PsTerminateSystemThread(STATUS_SUCCESS);
    ObDereferenceObject(g_pWorkerThreadObj);
    g_pWorkerThreadObj = NULL;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Exit");
}

void GetStreamData(
    NET_BUFFER_LIST* pNetBufferList,
    BYTE* pDataBuffer,
    UINT32 nDataBufferLength,
    UINT32* pByteCopied
    )
{
    NET_BUFFER_LIST*    pCurrentNetBufferList = pNetBufferList;
    NET_BUFFER_LIST*    pNextNetBufferList;
    NET_BUFFER*         pCurrentNetBuffer;
    NET_BUFFER*         pNextNetBuffer;
    BYTE*               pCurrentDataPointer = pDataBuffer;
    BYTE*               pNbDataPointer;
    UINT32              nCurrentBufferLength, nCurrentDataLength = 0;
    BOOLEAN             bBufferDone = FALSE;
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DNSFILTER, "%!FUNC! Entry");
    while (NULL != pCurrentNetBufferList)
    {
        pNextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(pCurrentNetBufferList);
        pCurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(pCurrentNetBufferList);
        while(NULL != pCurrentNetBuffer)
        {
            pNextNetBuffer = NET_BUFFER_NEXT_NB(pCurrentNetBuffer);
            nCurrentBufferLength = NET_BUFFER_DATA_LENGTH(pCurrentNetBuffer);
            nCurrentDataLength += nCurrentBufferLength;
            if(nCurrentDataLength > nDataBufferLength)
            {
                bBufferDone = TRUE;
                nCurrentBufferLength -= nCurrentDataLength - nDataBufferLength;
            }
            if(nCurrentBufferLength > 0)
            {
                pNbDataPointer = (BYTE*)NdisGetDataBuffer(
                    pCurrentNetBuffer,
                    nCurrentBufferLength,
                    pCurrentDataPointer,
                    0,
                    0
                    );
                ASSERT(NULL != pNbDataPointer);
                if(pNbDataPointer != pCurrentDataPointer)
                {
                    RtlCopyMemory(
                        pCurrentDataPointer,
                        pNbDataPointer,
                        nCurrentBufferLength
                        );
                }
                pCurrentDataPointer += nCurrentBufferLength;
            }
            if(bBufferDone)
            {
                *pByteCopied = nDataBufferLength;
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    TRACE_DNSFILTER,
                    "%!FUNC! Exit (bBufferDone); nCurrentDataLength = %u",
                    nCurrentDataLength
                    );
                return;
            }
            pCurrentNetBuffer = pNextNetBuffer;
        }
        pCurrentNetBufferList = pNextNetBufferList;
    }
    *pByteCopied = nCurrentDataLength;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSFILTER,
        "%!FUNC! Exit; nCurrentDataLength = %u",
        nCurrentDataLength
        );
}
